
本文介绍如何利用 java stream 和 collectors.groupingby 高效提取对象列表中基于指定属性(如 osname)的全部重复元素,无需重写 equals/hashcode,代码简洁且具备生产可用性。
本文介绍如何利用 java stream 和 collectors.groupingby 高效提取对象列表中基于指定属性(如 osname)的全部重复元素,无需重写 equals/hashcode,代码简洁且具备生产可用性。
在实际开发中,我们常需从对象集合中识别并提取“逻辑重复”项——即某些字段值相同但对象本身不相等的实例。以 Computer 类为例,其未重写 equals() 和 hashCode(),因此无法直接用 Set 去重或 distinct() 判断;但业务上我们关心的是 osName 相同的所有计算机,无论 id 是否不同。
此时,最清晰、高效且符合函数式编程思想的方案是:先按目标属性分组,再筛选出组内元素数大于 1 的所有条目,最后扁平化为单一列表。
以下是完整实现(Java 16+,支持 .toList();若使用 Java 11–15,请替换为 collect(Collectors.toList())):
List<Computer> duplicated = computers.stream()
.collect(Collectors.groupingBy(Computer::getOsName))
.entrySet().stream()
.filter(entry -> entry.getValue().size() > 1)
.flatMap(entry -> entry.getValue().stream())
.toList();✅ 执行逻辑解析:
立即学习“Java免费学习笔记(深入)”;
- groupingBy(Computer::getOsName) 将原始列表按 osName 分组,生成 Map<String, List<Computer>>,例如:
"Window10" → [c1, c4, c5],
"Window11" → [c2],
"Linux" → [c3]; - filter(e -> e.getValue().size() > 1) 仅保留含 ≥2 个元素的分组(即存在重复的 OS);
- flatMap(...stream()) 将每个匹配分组的 List<Computer> 展开为流式元素,最终合并为单一流;
- toList() 收集结果——顺序与原始列表中首次出现该 osName 的位置一致(因 groupingBy 默认保持插入顺序)。
⚠️ 注意事项:
- 该方法不修改原列表,也不依赖对象的 equals/hashCode,完全基于属性值,安全可靠;
- 若需去重后仅保留每组一个代表(如首个),可改用 Collectors.toMap(..., (a,b) -> a) 或 distinct() 配合自定义 key;
- 对于超大数据集(百万级),可考虑并行流(.parallelStream()),但注意 groupingBy 在并行下默认不保证分组内顺序,如需有序请显式使用 Collectors.groupingByConcurrent() + 后续排序;
- 若 getOsName() 可能返回 null,建议先用 Objects.requireNonNull() 或 filter(Objects::nonNull) 预处理,避免 NullPointerException。
总结:该方案以声明式风格精准表达业务意图——“找出所有在 osName 上重复的计算机”,兼具可读性、健壮性与性能,是 Java Stream 处理属性级重复问题的标准实践。


















