Java 8 Stream API 中 distinct() 与 distinctByKey() 的区别是什么?如何使用它们来过滤重复元素?

在Java 8中,Stream API是一个强大的工具,它允许以声明性的方式处理数据集合,使得代码更加简洁且易于理解。distinct()distinctByKey()是两种用于过滤重复元素的方法,虽然后者不是标准库直接提供的,但可以通过一些技巧实现类似功能。

1. distinct()方法

首先,让我们从distinct()开始。这个方法是Stream API的一个内置中间操作,它的主要职责是从数据流中移除重复的元素。当你调用distinct()时,Stream会检查每个元素的equals()hashCode()方法来确定哪些元素是重复的。如果两个元素通过equals()比较结果为真,并且它们的hashCode()相同,那么在结果中就只会保留其中一个。这意味着,对于基本类型和实现了这些方法的自定义对象,distinct()能很好地工作。

使用示例: 假设你有一个整数列表,想要去除重复的数字:

复制代码
1List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
2List<Integer> uniqueNumbers = numbers.stream().distinct().collect(Collectors.toList());

在这个例子中,uniqueNumbers将只包含1, 2, 3, 4, 5

2. distinctByKey()的实现

distinctByKey()并不是Stream API直接提供的方法,但它是一个常见的需求,特别是在处理复杂对象时,我们可能只想基于对象的某个特定属性去重。要实现这样的功能,我们可以借助于Collectors.groupingBy()或者利用TreeSet的自然排序和去重能力,或者通过自定义Predicate来实现。

示例:基于属性去重 假设有这样一个Person类,包含姓名(name)和年龄(age)属性,我们想基于姓名去重:

复制代码
1class Person {
2    String name;
3    int age;
4    // 构造器、getters、setters省略
5}
6
7List<Person> people = ... // 初始化人员列表

我们可以这样实现基于姓名的去重:

复制代码
1Function<Person, String> byName = Person::getName;
2Set<Person> uniquePeopleByName = people.stream()
3    .collect(Collectors.collectingAndThen(
4        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(byName))),
5        ArrayList::new));
6
7// 或者使用filter方法配合自定义Predicate实现类似逻辑
8Predicate<Person> distinctByKey = new Predicate<Person>() {
9    Set<String> seen = ConcurrentHashMap.newKeySet();
10    @Override
11    public boolean test(Person person) {
12        return seen.add(person.getName());
13    }
14};
15List<Person> uniquePeopleByNameFilter = people.stream()
16    .filter(distinctByKey)
17    .collect(Collectors.toList());

在这段代码中,我们利用了TreeSet的排序和唯一性来确保每个名字只出现一次。而第二种方法则是通过自定义的Predicate,利用ConcurrentHashMap的键唯一性来实现去重。

  • distinct()是Stream API提供的一个内置方法,它利用对象的equals()hashCode()方法去除流中的重复元素,适用于基本类型和实现了这些方法的对象。
  • distinctByKey()虽然不是标准API的一部分,但通过一些技巧,我们可以实现在复杂对象集合中根据某个属性去重的功能,这在处理具有多个属性的对象时非常有用。
  • 实现distinctByKey()时,选择合适的方法(如使用TreeSet或自定义Predicate)取决于具体的需求和场景,但核心思想都是利用某种形式的键值唯一性来达到去重的目的。

希望这样的解释能帮助你理解distinct()和如何实现类似distinctByKey()的功能,以及它们在处理数据集合时的应用。

相关推荐
学测绘的小杨10 小时前
CompassFusion:一个从 GNSS 到 GNSS/INS 组合导航的独立工程包
python
zzzzzz31016 小时前
当产品经理说这个很简单:我用Python自动化处理奇葩需求的实战指南
python·pycharm·产品经理
雪隐17 小时前
个人电脑玩AI-06让5060 Ti给你打工——不光能画画,Qwen3-TTS还能学人说话,连我老板都信了!
人工智能·后端·python
兵慌码乱1 天前
面向桌面端的资产管理系统分层架构设计与核心模块实现
python·系统架构·sqlite·pyqt5·数据库设计·桌面应用开发·mvc架构
hboot1 天前
AI工程师第三课 - 机器学习基础
python·scikit-learn·kaggle
顾林海1 天前
Agent入门阶段-编程基础-Python:流程控制
python·agent·ai编程
呱呱复呱呱2 天前
Django CBV 源码解读:一个请求是怎么找到你的 get() 方法的
python·django
曲幽2 天前
刚部署的 LibreTranslate 频频翻车?我掏出了 20 年前的 StarDict 词典,用 FastAPI 搭了个本地词典翻译 API
python·fastapi·web·translate·goldendict·libretranslate·stardict·pystardict
荣码2 天前
用Streamlit给AI应用套个界面,10行代码出Web页面
java·python
兵慌码乱2 天前
基于Python+PyQt5+SQLite的药房管理系统实现:事务一致性与界面解耦全流程解析
python·sqlite·信号与槽·pyqt5·数据库设计·桌面应用开发·事务处理