Java Stream 去重的多种方法

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

1. 使用 distinct() 方法

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

java 复制代码
List<Person> uniquePersons = persons.stream()
                                    .distinct()
                                    .collect(Collectors.toList());

2. 根据对象的属性去重

方法一:使用 Collectors.toMap

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

java 复制代码
// 保留第一个出现的元素
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

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

java 复制代码
// 并行流去重(不保证顺序)
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

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

java 复制代码
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. 根据字符串长度去重示例

java 复制代码
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 来记录已经出现过的元素,然后过滤掉重复的元素。

java 复制代码
//定义一个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);

总结

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

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

相关推荐
apihz几秒前
随机驾考题目(C 照科一 / 科四 2000+ 题)免费API调用教程
android·java·c语言·开发语言·网络协议·tcp/ip
AIGS00116 分钟前
企业AI落地:Agent OS 治理框架实践
java·大数据·人工智能·机器学习·人工智能ai大模型应用
matlab代码25 分钟前
基于matlab人脸疲劳驾驶检测系统(可使用其它人脸视频)【源码39期】
开发语言·matlab·人脸疲劳检测
-西门吹雪1 小时前
现代C++ 并发编程-学习指南
java·jvm·c++
柒和远方1 小时前
LeetCode 4. 寻找两个正序数组的中位数 —— 二分划分的艺术
javascript·python·算法
lh17931 小时前
数据产业服务分类(53)——分类设计——数据能力集成
java·人工智能·分类
zenithdev11 小时前
fastcache:为 Go 设计的低 GC 压力内存缓存
开发语言·其他·缓存·golang
想你依然心痛1 小时前
自定义RTOS内核:从零实现上下文切换与任务调度——汇编、PendSV
java·开发语言·汇编·pendsv
geovindu2 小时前
java:Abstract Factory Pattern
java·开发语言·后端·设计模式·抽象工厂模式·创建型模式
geovindu2 小时前
java: Factory Method Pattern
java·开发语言·后端·设计模式·工厂方法模式·创建型模式