当前位置:

首页 > 编程开发 > Java8如何通过Stream对list对象某个属性去重

Java8如何通过Stream对list对象某个属性去重

Listary 5 Pro
Listary 5 Pro

Listary 5是一款最快的文件搜索工具之一。跟其他工具相比Listary更强大的的地方是跟您工作流程的无缝集成:无论您是在资源管理器中整理文件,或者是正在Photoshop中进行设计,任何情况下只要您需要访问文件,Listary就能将您需要的结果通过您喜欢的方式快速且精确的展示

立即下载
2026-08-22
系统应用 系统工具 Listary 路径

前言 在实际开发中,List集合去重是个再常见不过的需求了——数据量一大、来源一杂,重复记录就冒出来了。很多时候需要的不是花哨的算法,而是一套干净、高效、能直接上手用的解决方案。 这里把平时学习和实践中遇到的几种典型场景做了整理,重点围绕Ja va8的Stream流操作和TreeSet的天然去重特性

前言

在实际开发中,List集合去重是个再常见不过的需求了——数据量一大、来源一杂,重复记录就冒出来了。很多时候需要的不是花哨的算法,而是一套干净、高效、能直接上手用的解决方案。

这里把平时学习和实践中遇到的几种典型场景做了整理,重点围绕Ja va8的Stream流操作和TreeSet的天然去重特性来展开。当然,方法本身只是工具,最终目的是在保证效率的前提下把事情做对,没必要拘泥于某一种固定写法。

正文

数据对象

为了方便验证,先准备一个静态不可变的List,里面放了四条SyncBalance数据,其中有些字段完全重复,有些某个字段重复——正好覆盖多种去重场景。

private static final List list = asList(
        new SyncBalance(BigDecimal.ZERO, "12345678", 0, 1, "GGBond", 32, 0),
        new SyncBalance(BigDecimal.ZERO, "22345678", 0, 1, "GGBond", 32, 0),
        new SyncBalance(BigDecimal.ZERO, "22345678", 1, 1, "GGBond", 32, 0),
        new SyncBalance(BigDecimal.ZERO, "22345678", 1, 1, "GGBond", 32, 0)
);

1. 对象整体去重

最直接的需求:所有属性都一样才算重复,去掉之后只保留一条。Stream自带的distinct()方法恰好就是干这个的——基于equals()和hashCode()去重,前提是对象正确实现了这两个方法。

/**
 * 1. 对所有属性一样的数据进行去重
 */
public static void allColumnDistinct() {
    List allColumnDistinct = list.stream().distinct().collect(Collectors.toList());
}

2. 单个属性去重(只返回属性)

有时候我们不关心整个对象,只需要拿到去重后的某个字段列表,比如所有不重复的手机号。用map先提取属性,再distinct即可。

/**
 * 2. 对单个属性去重,只返回去重的属性
 */
public static void columnDistinct() {
    List columnDistinct = list.stream().map(SyncBalance::getMobile).distinct().collect(Collectors.toList());
}

3. 单个属性去重

如果既要按某个字段去重,又要保留完整对象呢?这时候Stream自带的distinct就无能为力了,需要借助TreeSet或者自定义的Predicate过滤器。下面给出三种常见写法,各有侧重。

/**
 * 3-1. 对单个属性一样的数据进行去重,下面是对`mobile`去重
 * 利用collectingAndThen + TreeSet,排序规则按mobile
 */
public static void oneColumnDistinctMethodOne() {
    List oneColumnDistinctMethodOne = list.stream().collect(Collectors.collectingAndThen(
            Collectors.toCollection(() -> new TreeSet<>(
                    Comparator.comparing(SyncBalance::getMobile))), ArrayList::new));
}
/**
 * 3-2. 对单个属性一样的数据进行去重,下面是对`mobile`去重(通过自定义属性去重方法)
 */
public static void oneColumnDistinctMethodTwo() {
    List oneColumnDistinctMethodTwo = list.stream().filter(distinctByKey(i -> i.getMobile())).collect(Collectors.toList());
}

/**
* list对象单个字段去重(自定义属性)
* @param keyExtractor 去重的对象字段
* @return
* @param 
*/
public static  Predicate distinctByKey(Function keyExtractor) {
    Map seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
/**
 * 3-3. 对单个属性一样的数据进行去重,下面是对`mobile`去重
 * 直接用TreeSet逐个添加,简单暴力
 */
public static void oneColumnDistinctMethodThree() {
    TreeSet oneColumnDistinctMethodThree = new TreeSet<>(Comparator.comparing(i -> i.getMobile()));
    list.forEach(a -> oneColumnDistinctMethodThree.add(a));
}

4. 多个属性去重

当去重条件变成多个字段组合时,思路依然是用TreeSet,只需要把Comparator的规则改为多个字段拼接或链式比较。这里用手机号+状态两个字段举例。

/**
 * 4. 多个字段条件去重
 */
public static void twoColumnDistinct() {
    List twoColumnDistinct = list.stream().collect(Collectors.collectingAndThen(
            Collectors.toCollection(() -> new TreeSet<>(
                    Comparator.comparing(p -> p.getMobile() + ";" + p.getStatus()))), ArrayList::new));
}

验证方法

在主方法中依次调用上面定义的去重方法,并在每个方法的前后打印分隔标记,便于观察结果。

/**
 * 验证的main方法
 * @param args
 */
public static void main(String[] args) {
    allColumnDistinct();
    columnDistinct();
    oneColumnDistinctMethodOne();
    oneColumnDistinctMethodTwo();
    oneColumnDistinctMethodThree();
    twoColumnDistinct();
}

运行结果

allColumnDistinct---v

SyncBalance(accountBalance=0, mobile=12345678, status=0, sysId=1, loginAccountName=GGBond, keepOrUpdate=32, id=0)

SyncBalance(accountBalance=0, mobile=22345678, status=0, sysId=1, loginAccountName=GGBond, keepOrUpdate=32, id=0)

SyncBalance(accountBalance=0, mobile=22345678, status=1, sysId=1, loginAccountName=GGBond, keepOrUpdate=32, id=0)

allColumnDistinct---^

columnDistinct---v

12345678

22345678

columnDistinct---^

oneColumnDistinctMethodOne---v

SyncBalance(accountBalance=0, mobile=12345678, status=0, sysId=1, loginAccountName=GGBond, keepOrUpdate=32, id=0)

SyncBalance(accountBalance=0, mobile=22345678, status=0, sysId=1, loginAccountName=GGBond, keepOrUpdate=32, id=0)

oneColumnDistinctMethodOne---^

oneColumnDistinctMethodTwo---v

SyncBalance(accountBalance=0, mobile=12345678, status=0, sysId=1, loginAccountName=GGBond, keepOrUpdate=32, id=0)

SyncBalance(accountBalance=0, mobile=22345678, status=0, sysId=1, loginAccountName=GGBond, keepOrUpdate=32, id=0)

oneColumnDistinctMethodTwo---^

oneColumnDistinctMethodThree---v

SyncBalance(accountBalance=0, mobile=12345678, status=0, sysId=1, loginAccountName=GGBond, keepOrUpdate=32, id=0)

SyncBalance(accountBalance=0, mobile=22345678, status=0, sysId=1, loginAccountName=GGBond, keepOrUpdate=32, id=0)

oneColumnDistinctMethodThree---^

twoColumnDistinct---v

SyncBalance(accountBalance=0, mobile=12345678, status=0, sysId=1, loginAccountName=GGBond, keepOrUpdate=32, id=0)

SyncBalance(accountBalance=0, mobile=22345678, status=0, sysId=1, loginAccountName=GGBond, keepOrUpdate=32, id=0)

SyncBalance(accountBalance=0, mobile=22345678, status=1, sysId=1, loginAccountName=GGBond, keepOrUpdate=32, id=0)

twoColumnDistinct---^​

Ja va8如何通过Stream对list对象某个属性去重

结尾

集合去重的思路远不止Stream和TreeSet这两种,只要能保证效率和正确性,用HashSet、LinkedHashMap甚至数据库去重都行。关键是理解每种场景下“去重”到底意味着什么——是整个对象、某个字段、还是字段组合?搞清楚需求,选对工具,代码自然干净利落。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
作者最新文章
编程开发
相关文章 更多
listview的优化 基础知识整理:新手先看这篇
listview的优化 基础知识整理:新手先看这篇

ListView优化的核心在于视图复用机制,避免在getView中频繁创建视图。使用ViewHolder模式可减少findViewById耗时。数据与图片应异步加载,滑动时暂停加载以提升流畅度。简化列表项布局层级,减少嵌套。大数据集采用分页加载,并优先使用精确的局部数据更新方法,避免全局刷新。

css列表标签list与表格标签table详解
css列表标签list与表格标签table详解

CSS列表样式通过list-style-type属性控制标记类型,list-style-image属性控制标记图像,list-style-position属性控制标记位置,三者可合并简写为list-style属性;表格样式使用border属性设置边框,border-collapse属性合并相邻边框,并支持宽度、高度及文本对齐设置,从而使页面结构清晰美观。

java中arraylistsetmap对比及分析
java中arraylistsetmap对比及分析

Java四种集合各具特性:数组长度固定,适合随机访问但不可变长;List动态数组,支持灵活增删改查;Set元素唯一,常用HashSet无序、TreeSet自然排序;Map键值映射,键唯一,常用HashMap无序、TreeMap按键有序;按需选型,能有效避免踩坑并提升性能。

C++之vector/list/map完整对比与解读
C++之vector/list/map完整对比与解读

vector底层连续数组,支持常数时间随机访问和尾部增删;list基于双向链表,任意位置增删为常数时间,但无随机访问;map采用红黑树,按键自动排序,插入删除查找均为对数时间。这些特点使它们适用于不同需求。

C++之std::initializer_list的使用小结
C++之std::initializer_list的使用小结

std::initializer_list是C++11引入的轻量级数据视图,支持花括号初始化容器与自定义类。其内部通过两个const指针实现只读访问,不持有数据,生命周期受限。它简洁地解决了同类型可变参数问题,在STL容器初始化及函数参数中广泛应用,提升了代码可读性与安全性。

Java中的各种list有什么区别及list和set区别详析
Java中的各种list有什么区别及list和set区别详析

Java中List常见实现:ArrayList(基于动态数组,查询速度快)、LinkedList(基于双向链表,头尾操作效率高)、CopyOnWriteArrayList(写时复制,适合高并发读多写少),Vector与Stack已淘汰。List有序、可重复、有下标;Set无序、不可重复、无下标,依赖于hashCode与equals去重。

如何在Nginx中通过Unix Socket连接PHP-FPM_修改listen为sock文件
如何在Nginx中通过Unix Socket连接PHP-FPM_修改listen为sock文件

PHP-FPM与Nginx通过Unix Socket连接:避开三个高频陷阱 想让Nginx和PHP-FPM通过Unix Socket通信,从而获得比TCP更快的速度和更好的安全性?想法很好,但配置过程里,几个细节没对齐,502错误或者权限拒绝(Permission denied)就会立刻找上门。下面

C++ list与vector区别及性能对比
C++ list与vector区别及性能对比

答案:C++中list为双向链表,适合频繁插入删除;vector为动态数组,支持随机访问且缓存友好。list插入删除O(1),但内存开销大;vector随机访问O(1),遍历更快,扩容时有复制成本。多数场景优先选用vector。

实战经验分享:深入解析Java集合框架应用及问题解决
实战经验分享:深入解析Java集合框架应用及问题解决

1.ListList是Java集合框架中的一种有序集合,允许元素重复,并且可以通过索引来访问元素。List的常用实现类包括ArrayList、LinkedList和Vector。以下演示如何使用ArrayList来存储一组整数:importjava.util.ArrayList;publicclassArrayListDemo{publicstaticvoidmain(String[]args){//创建一个ArrayList对象ArrayList<Integer>numbers=newArr

Java集合框架面试必备:突破算法与数据结构,成功拿下心仪offer
Java集合框架面试必备:突破算法与数据结构,成功拿下心仪offer

Java集合框架是Java编程语言中用于存储和操作数据的核心组件,提供了丰富的集合类型和相关操作。在Java面试中,集合框架是考察的重点之一,掌握集合框架的原理和用法,能够帮助你轻松应对面试,斩获心仪offer。1.集合框架概述Java集合框架提供了各种各样的集合类型,如List、Set、Map和Queue,每种类型都有其独特的特点和用途。List是有序的元素集合,可以重复元素;Set是有序的元素集合,不能重复元素;Map是键值对集合,键是唯一的,值可以是重复的;Queue是先进先出的元素集合。2.算法和

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

PDF教程
PDF教程

正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

Mac软件 更多
Postman macOS版
Postman macOS版
Mac

Postman是一套面向API全生命周期的开发与测试平台,可在Mac上发送和调试多种协议请求,管理集合、环境及认证信息,并完成脚本测试、模拟服务、监控、文档发布和团队协作。它适合开发、测试及运维人员使用,提供个人免费方案。

Docker Desktop macOS版
Docker Desktop macOS版
Mac

Docker Desktop 是 Docker 官方为 Mac 提供的容器开发环境,集成 Docker Engine、命令行工具、Compose、Build、Kubernetes 与图形化管理界面。

Android Studio macOS版
Android Studio macOS版
Mac

Android Studio 是 Google 面向安卓应用开发提供的官方集成开发环境,基于 IntelliJ IDEA 构建。它整合智能代码编辑、Gradle 构建、界面预览、设备模拟、性能分析、调试测试及 APK 分析等工具。

WINDOWS 更多
Postman Windows版
Postman Windows版
Windows

Postman 是面向 API 全生命周期的开发与协作平台。Windows 桌面版可用于构建和发送 HTTP、GraphQL、gRPC、WebSocket、MQTT 等请求,并提供环境变量、集合、脚本测试、模拟服务、监控、文档及团队工作区等

Docker Desktop Windows版
Docker Desktop Windows版
Windows

Docker Desktop 为 Windows 开发者提供集成式容器环境,内置 Docker Engine、命令行工具、Compose、Build 与 Kubernetes 等组件,可在本机完成镜像构建、容器运行、日志查看和资源管理。

Android Studio Windows版
Android Studio Windows版
Windows

Android Studio 是 Google 面向 Android 应用开发提供的官方集成开发环境,以 IntelliJ IDEA 为基础,集成智能代码编辑、Gradle 构建、设备模拟、界面预览、调试测试及性能分析工具,

即将离开本站
您即将前往第三方网站,请确认是否继续?