java8 list中根据对象多属性去重

本文深入探讨了Java8中Stream API的高级应用,特别是如何利用collectingAndThen方法实现根据多个字段进行去重,以及使用groupingBy方法进行分组。通过具体案例,详细讲解了在复杂业务场景下,如何优化数据处理流程,提高代码效率。

强烈推荐一个大神的人工智能的教程:http://www.captainbed.net/zhanghan
原文地址:https://blog.youkuaiyun.com/zh15732621679/article/details/80483617

项目需求描述
出于项目需要,有一个list,实体包含多个字段,当其中两个字段相同均相同时,就认为list中的两条记录是一样的,本来想通过分组实现,java8的分组只提供根据一个字段分组,只好另寻他路,java8有一个collectingAndThen可以根据多个字段去重,因为我们的需求是可以去重之后操作,因此采用这种方式。

分组及去重
  • 分组
classEntities.stream().collect(Collectors.groupingBy(ClassEntity::getGrade));
  • 1
  • java8去重(根据年级和专业,当年级和专业都相同的情况下看做是重复数据)
List<ClassEntity> distinctClass = classEntities.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getProfessionId() + ";" + o.getGrade()))), ArrayList::new));
  • 1
  • 通过hashSet去重(如将classNames去重):该种去重是bean完全相同的时候算重复数据
List<String> classNameList = new ArrayList(new HashSet(classNames));

  • 1
  • 2

转载于:https://www.cnblogs.com/java-hardly-road/p/11395976.html

<think>好的,用户的问题是想在Java中使用Stream API根据List对象的某个属性进行。首先,我需要确认用户的具体需求。他们有一个包含对象的列表,每个对象可能有多个属性,他们希望根据其中一个属性来,保留该属性唯一的对象。 接下来,我需要回忆Java Stream API中的distinct()方法。默认情况下,distinct()是基于对象的equals和hashCode方法来判断是否复的。所以如果用户没有写这些方法,直接使用distinct()是无法根据特定属性的。用户需要自定义逻辑。 然后,我需要考虑如何实现根据属性。常见的方法包括使用filter配合Set来跟踪已出现的属性值,或者使用Collectors.toMap将对象映射到属性,并处理冲突。另外,可能还有第三方库如Vavr提供的API,但用户可能更倾向于使用标准库。 第一种方法,使用filter和ConcurrentHashMap。通过维护一个并发的Set来记录已经出现的属性值,这样可以线程安全地在流处理中判断是否复。例如,使用ConcurrentHashMap.newKeySet()来创建线程安全的集合,然后在filter中检查属性是否已经存在,如果不存在则添加到集合中,并保留该元素。 第二种方法,使用Collectors.toMap。将流中的元素收集到一个Map中,键是对象的属性,值是该对象本身。当遇到复键时,可以选择保留现有的或新的元素。然后提取Map的values作为后的列表。这种方法简洁,但需要注意处理冲突的方式,比如使用(first, second) -> first来保留第一个出现的元素。 第三种方法是使用自定义的收集器,比如通过Collectors.collectingAndThen来包装toMap的结果,直接转换为List。这可能更高效,因为直接操作Map的结构,避免中间步骤。 另外,需要提醒用户关于这些方法的注意事项。例如,如果使用toMap,默认情况下遇到复键会抛出异常,所以必须提供合并函数。另外,保持顺序可能需要使用LinkedHashMap来维持元素的原始顺序。此外,性能方面,对于大数据量的处理,不同的方法可能有不同的效率,需要根据实际情况选择。 用户可能还需要知道如何应用到具体的对象。比如,用户有一个User类,有id、username等属性,他们可能想根据username。这时候需要调整lambda表达式中的方法引用,如User::getUsername。 同时,引用用户提供的参考资料,特别是引用[1]中提到的ConcurrentHashMap,可能相关于线程安全的问题,但在这个场景下,使用ConcurrentHashMap.newKeySet()主要是为了线程安全地跟踪已出现的属性值,确保并行流中的正确性。不过,如果用户使用的是顺序流,可能不需要并发结构,但为了兼容并行流,建议使用线程安全的集合。 最后,总结几种方法,给出示例代码,并说明各自的优缺点,帮助用户根据具体情况选择最合适的方案。同时,生成相关问题,引导用户进一步学习或解决可能遇到的扩展问题,比如保持顺序、处理大数据量效率、自定义比较逻辑等。</think>在Java中使用Stream API根据对象属性,可以通过以下三种方法实现: ### 方法一:使用filter维护状态集合(线程安全) ```java List<User> distinctUsers = users.stream() .filter(distinctByKey(User::getUsername)) .collect(Collectors.toList()); private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) { Set<Object> seen = ConcurrentHashMap.newKeySet(); return t -> seen.add(keyExtractor.apply(t)); } ``` 通过`ConcurrentHashMap.newKeySet()`创建线程安全的集合跟踪已出现的属性值[^1] ### 方法二:使用toMap收集器(推荐方式) ```java List<User> distinctUsers = users.stream() .collect(Collectors.toMap( User::getUsername, Function.identity(), (first, second) -> first )) .values() .stream() .collect(Collectors.toList()); ``` 通过保留第一个出现的复元素实现 ### 方法三:自定义收集器(高效实现) ```java List<User> distinctUsers = users.stream() .collect(Collectors.collectingAndThen( Collectors.toMap( User::getUsername, Function.identity(), (a,b) -> a, LinkedHashMap::new ), map -> new ArrayList<>(map.values()) )); ``` 使用`LinkedHashMap`可保持原始顺序 ### 注意事项: 1. `toMap`方法默认使用`HashMap`,不保证顺序 2. 需要根据业务需求选择保留第一个或最后一个复元素 3. 如果属性值为null需要特殊处理 4. 建议将逻辑封装为工具方法复用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值