Java Stream去重常见的多种方法及示例

 更新时间:2025年04月12日 10:59:42   作者:码农界赵子龙  
这篇文章主要介绍了Java Stream中实现去重的多种方法,包括使用distinct()、Collectors.toMap、filter和groupingBy等,详细说明了每种方法的应用场景和示例代码,文中通过代码介绍的非常详细,需要的朋友可以参考下

在Java Stream中实现去重有多种方法,具体取决于需求和场景。以下是常见的几种方法及示例:

1. 使用 distinct() 方法

适用于对象已正确实现 equals() 和 hashCode(),基于对象整体去重并保留顺序:

List<Person> uniquePersons = persons.stream()
                                    .distinct()
                                    .collect(Collectors.toList());

2. 根据对象的属性去重

方法一:使用 Collectors.toMap

根据属性作为键,保留第一个或最后一个元素,支持顺序(使用 LinkedHashMap):

// 保留第一个出现的元素
List<Person> uniqueByName = persons.stream()
    .collect(Collectors.toMap(
        Person::getName,
        Function.identity(),
        (oldP, newP) -> oldP, // 保留旧值(第一个)
        LinkedHashMap::new    // 保持插入顺序
    ))
    .values().stream()
    .collect(Collectors.toList());

// 保留最后一个出现的元素
List<Person> uniqueByNameLast = persons.stream()
    .collect(Collectors.toMap(
        Person::getName,
        Function.identity(),
        (oldP, newP) -> newP  // 保留新值(最后一个)
    ))
    .values().stream()
    .collect(Collectors.toList());

方法二:使用 filter 和线程安全的 Set

适用于并行流,但可能不保留顺序:

// 并行流去重(不保证顺序)
Set<String> seen = ConcurrentHashMap.newKeySet();
List<Person> uniqueByName = persons.parallelStream()
    .filter(p -> seen.add(p.getName()))
    .collect(Collectors.toList());

// 顺序流去重(保留顺序)
Set<String> seenOrdered = new HashSet<>();
List<Person> uniqueByNameOrdered = persons.stream()
    .filter(p -> seenOrdered.add(p.getName()))
    .collect(Collectors.toList());

方法三:使用 groupingBy

分组后取每组的第一个元素,保持顺序:

List<Person> uniqueByName = persons.stream()
    .collect(Collectors.groupingBy(
        Person::getName,
        LinkedHashMap::new,    // 保持插入顺序
        Collectors.toList()
    ))
    .values().stream()
    .map(group -> group.get(0)) // 取第一个元素
    .collect(Collectors.toList());

3. 根据字符串长度去重示例

List<String> words = Arrays.asList("apple", "banana", "orange", "grape", "kiwi");
List<String> uniqueByLength = words.stream()
    .collect(Collectors.toMap(
        String::length,
        Function.identity(),
        (oldVal, newVal) -> oldVal,
        LinkedHashMap::new
    ))
    .values().stream()
    .collect(Collectors.toList());
// 结果: ["apple", "banana", "kiwi"](保留顺序)

4. 自定义去重借助Filter 实现:

自定义一个 Predicate 函数,用一个 Set 来记录已经出现过的元素,然后过滤掉重复的元素。

//定义一个Predicate函数
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Set<Object> sets = ConcurrentHashMap.newKeySet();
    return t -> sets.add(keyExtractor.apply(t));
}

//根据age属性去重
list.stream().filter(distinctByKey(s -> s.getAge()))
        .forEach(System.out::println);

附:java中使用stream流根据对象中的某一字段进行去重

在开发中经常会遇到数据去重的,单个基本类型的集合去重比较容易,比如String、Integer,直接使用流中的distinct方法去重即可。但是遇到对象集合,需要使用对象中的某个字段去重就不能使用这个方法了。可以在流式编程中加入TreeSet,TreeSet是一个有序且不重复的有序集合。以用户User数据位列:

List<User> list = userService.list();
        list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing( f -> f.getDeptName()+":"+f.getStatus()))),ArrayList::new));

这里是根据用户的部门以及用户的状态进行数据去重,将用户的部门以及状态拼接位一个字符串进行去重,这样stream内部会将整个user集合数据组装成这样进行整体去重,这样去重下来的数据就是,每一个部门中只会存在一个状态的部门人员数据,这里是举个例子,在实际环境中,你可以将User换成自己想要去重的实体集合。

总结

  • distinct():简单高效,适用于对象整体去重。
  • toMap 或 groupingBy:灵活,支持按属性去重,可控制保留顺序。
  • filter + Set:适合并行流,但需注意线程安全和顺序问题。

根据具体场景选择最合适的方法,确保代码简洁且性能良好。

到此这篇关于Java Stream去重常见的多种方法及示例的文章就介绍到这了,更多相关Java Stream去重方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Maven导入依赖时爆红的几种解决方法

    Maven导入依赖时爆红的几种解决方法

    使用idea建立maven项目,maven导入依赖报红,本文主要介绍了Maven导入依赖时爆红的几种解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • Java数组反转方式

    Java数组反转方式

    这篇文章主要介绍了Java数组反转方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • springboot基于过滤器实现接口请求耗时统计操作

    springboot基于过滤器实现接口请求耗时统计操作

    这篇文章主要介绍了springboot基于过滤器实现接口请求耗时统计操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • Java加权负载均衡策略实现过程解析

    Java加权负载均衡策略实现过程解析

    这篇文章主要介绍了Java加权负载均衡策略实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • SpringMVC框架实现上传图片的示例代码

    SpringMVC框架实现上传图片的示例代码

    本篇文章主要介绍了SpringMVC框架实现上传图片的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • SpringBoot处理 CORS 跨域的方法详解

    SpringBoot处理 CORS 跨域的方法详解

    Springboot跨域问题,是当前主流web开发人员都绕不开的难题,CORS是一个W3C标准,全称是”跨域资源共享”,本文将给大家详细介绍SpringBoot 如何处理 CORS 跨域,感兴趣的同学跟着小编一起来看看吧
    2023-07-07
  • 深度源码解析Java 线程池的实现原理

    深度源码解析Java 线程池的实现原理

    如何高效的使用这些资源就是程序员在平时写代码时候的一个努力的方向。本文要说的线程池就是一种对 CPU 利用的优化手段。对Java 线程池的实现原理相关知识感兴趣的朋友一起看看吧
    2021-05-05
  • Java通过模板替换实现excel的传参填写方式

    Java通过模板替换实现excel的传参填写方式

    文章介绍了使用Java模板替换功能来实现Excel文件的传参填写,通过将模板中的占位符(如$)进行转义和替换,可以动态生成包含具体数据的Excel文件,这种方法适用于需要批量生成带有个性化数据的Excel文档的场景
    2025-12-12
  • Spring线程池的配置及使用ThreadPoolTaskExecutor过程

    Spring线程池的配置及使用ThreadPoolTaskExecutor过程

    本文主要介绍了Spring提供的线程池ThreadPoolTaskExecutor及其配置方式,以及FixedThreadPool线程池的使用注意事项,同时强调了计数器的使用可以保证线程池执行顺序
    2026-03-03
  • Java实现String转Integer的四种方案对比

    Java实现String转Integer的四种方案对比

    在Java项目开发中,经常会有类型转换处理,这篇文章主要为大家详细介绍了Java实现String转Integer的四种方案对比,感兴趣的小伙伴可以了解下
    2026-03-03

最新评论