如何使用Java 8 Stream基于多属性获取列表中的唯一Java对象
嘿,刚好处理过类似的需求,给你分享几种基于Java 8 Stream的解决方案,完美解决按id+name组合去重的问题:
方法一:重写
Person类的equals()和hashCode(),配合Stream.distinct() 这是最基础的方案,因为Stream.distinct()默认依赖对象的equals()和hashCode()方法判断是否重复。我们需要把id和name作为判断相等的核心依据:
首先修改Person类:
class Person { Integer id; String name; // 构造器、getter/setter可根据需求补充 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return Objects.equals(id, person.id) && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(id, name); } }
然后直接用Stream的distinct()方法完成去重:
List<Person> uniquePersons = personList.stream() .distinct() .collect(Collectors.toList());
优缺点:
- 优点:代码简洁直观,后续再需要去重时直接调用
distinct()即可 - 缺点:如果
Person类是第三方库提供的、无法修改源码,这个方法就不适用了
方法二:利用
Collectors.toMap()实现无侵入式去重 如果不能修改Person类,这个方法就非常合适。我们可以把id和name的组合作为Map的key,Person对象作为value,遇到重复key时指定明确的保留规则:
List<Person> uniquePersons = personList.stream() .collect(Collectors.toMap( // 用id和name组成的列表作为唯一key(避免字符串拼接的潜在冲突) p -> Arrays.asList(p.getId(), p.getName()), // 保留原Person对象作为value Function.identity(), // 遇到重复key时,保留已经存在的元素(也可以换成replacement保留新元素) (existing, replacement) -> existing )) // 把Map的value转换成List .values() .stream() .collect(Collectors.toList());
小提示:如果担心Arrays.asList的性能,也可以自定义一个简单的组合类作为key(记得重写equals()和hashCode()),或者用字符串拼接(比如p.getId() + "|" + p.getName()),但字符串拼接要注意避免潜在的冲突(比如id=1、name="2|3"和id=12、name="3"会生成相同的key)。
方法三:自定义
Predicate配合Stream.filter() 这个方法更轻量,用一个Set来记录已经处理过的id+name组合,通过filter过滤掉重复元素:
// 用来记录已经出现过的id+name组合 Set<List<Object>> seen = new HashSet<>(); List<Person> uniquePersons = personList.stream() // Set.add()方法逻辑:元素不存在时返回true,存在时返回false,刚好适配filter的判断条件 .filter(p -> seen.add(Arrays.asList(p.getId(), p.getName()))) .collect(Collectors.toList());
注意:这个seen集合必须定义在Stream外部,不能在filter里新建,否则每次过滤都会创建新集合,完全起不到去重作用。
内容的提问来源于stack exchange,提问作者seasagar
相关产品推荐
相关产品推荐

