distinct() 应置于 limit(n) 前:先去重再截取,确保结果为前 n 个不重复元素;若顺序颠倒,则仅对前 n 个原始元素去重,可能遗漏更早出现的重复项或无法凑足 n 个唯一值。

Java Stream 的 distinct() 和 limit(n) 是两个常用中间操作,分别用于去重和截断流。它们可以单独使用,也能组合使用,但顺序很重要——distinct 应该放在 limit 前面,否则可能得不到预期的去重结果。
distinct:基于 equals + hashCode 去重
distinct() 会保留流中第一次出现的元素,后续重复元素会被过滤掉。它依赖元素类型的 equals() 和 hashCode() 方法(对于自定义对象,必须正确重写这两个方法)。
- 对基本包装类型(如
Integer、String)直接可用,因为 JDK 已实现规范的 equals/hashCode - 对自定义类,若未重写
equals和hashCode,默认按引用比较,同内容不同对象仍视为不重复 - 注意:distinct 是有状态操作,内部会维护一个 Set 来记录已见过的元素,对大数据量需留意内存开销
limit(n):取前 n 个元素
limit(n) 返回最多包含前 n 个元素的流。如果原流不足 n 个,就返回全部;如果 n ≤ 0,则返回空流。
- 它是短路操作(short-circuiting),一旦取够 n 个就会停止处理后续元素,提升效率
- 常用于分页、取样、避免全量处理等场景
- 与
skip(n)配合可实现“跳过前 m 个,再取 n 个”的分页逻辑
distinct 和 limit 组合使用的正确顺序
想先去重再取前几条?必须写成 stream.distinct().limit(n)。反过来写 stream.limit(n).distinct() 是错的——它只对前 n 个原始元素去重,可能漏掉后面更早出现的重复项。
- ✅ 正确:获取去重后前 3 个不同用户名 →
users.stream().map(User::getName).distinct().limit(3) - ❌ 错误:先 limit 再 distinct → 可能返回重复名,也可能没取到真正前 3 个唯一值
- ⚠️ 注意:distinct 不保证顺序稳定性(虽然通常保持输入顺序),但 limit 总是按流的自然顺序截取
实际小例子
比如有一组整数 List.of(1, 2, 2, 3, 3, 4, 5, 5):
-
list.stream().distinct().collect(toList())→[1, 2, 3, 4, 5] -
list.stream().limit(4).collect(toList())→[1, 2, 2, 3] -
list.stream().distinct().limit(4).collect(toList())→[1, 2, 3, 4] -
list.stream().limit(4).distinct().collect(toList())→[1, 2, 3](因前 4 个含重复,去重后只剩 3 个)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











