请直接移步原文Java中List集合对象去重及按属性去重的8种方法

只记录自己喜欢的几种方法

  • 对象元素整体去重的2种方法
  • 按照对象属性去重的4种方法

预备数据

public class ListRmDuplicate {private List<String> list;private List<Player> playerList;@BeforeEachpublic void setup() {list = new ArrayList<>();list.add("kobe");list.add("james");list.add("curry");list.add("zimug");list.add("zimug");playerList= new ArrayList<>();playerList.add(new Player("kobe","10000")); //科比万岁playerList.add(new Player("james","32"));playerList.add(new Player("curry","30"));playerList.add(new Player("zimug","27")); // 注意这里名字重复playerList.add(new Player("zimug","18")); //注意这里名字和年龄重复playerList.add(new Player("zimug","18")); //注意这里名字和年龄重复}
}

一、集合元素整体去重

List中的String类型以集合元素对象为单位整体去重。如果你的List放入的是Object对象,需要你去实现对象的equals和hashCode方法,去重的代码实现方法和List去重是一样的

方法一

是大家最容易想到的,先把List数据放入Set,因为Set数据结构本身具有去重的功能,所以再将SET转为List之后就是去重之后的结果。这种方法在去重之后会改变原有的List元素顺序,因为HashSet本身是无序的,而TreeSet排序也不是List种元素的原有顺序

@Test
void testRemove1() {/*Set<String> set = new HashSet<>(list);List<String> newList = new ArrayList<>(set);*///去重并排序的方法(如果是字符串,按字母表排序。如果是对象,按Comparable接口实现排序)//List<String> newList = new ArrayList<>(new TreeSet<>(list));//简写的方法List<String> newList = new ArrayList<>(new HashSet<>(list));System.out.println( "去重后的集合: " + newList);
}
方法二

使用就比较简单,先用stream方法将集合转换成流,然后distinct去重,最后在将Stream流collect收集为List。

@Test
void testRemove2() {List<String> newList = list.stream().distinct().collect(Collectors.toList());System.out.println( "去重后的集合: " + newList);
}

二、按照集合元素对象属性去重

其实在实际的工作中,按照集合元素对象整体去重的应用的还比较少,更多的是要求我们按照元素对象的某些属性进行去重。
看到这里请大家回头去看一下上文中,构造的初始化数据playerList,特别注意其中的一些重复元素,以及成员变量重复。

方法一

为TreeSet实现Comparator接口,如果我们希望按照Player的name属性进行排序,就去在Comparator接口中比较name。下文中写了两种实现Comparator接口方法:

  • lambda表达式:(o1, o2) -> o1.getName().compareTo(o2.getName())
  • 方法引用:Comparator.comparing(Player::getName)
@Test
void testRemove5() {//Set<Player> playerSet = new TreeSet<>((o1, o2) -> o1.getName().compareTo(o2.getName()));Set<Player> playerSet = new TreeSet<>(Comparator.comparing(Player::getName));playerSet.addAll(playerList);/*new ArrayList<>(playerSet).forEach(player->{System.out.println(player.toString());});*///将去重之后的结果打印出来new ArrayList<>(playerSet).forEach(System.out::println);
}

输出结果如下:三个zimug因为name重复,另外两个被去重。但是因为使用到了TreeSet,list中元素被重新排序。

Player{name=‘curry’, age=‘30’}
Player{name=‘james’, age=‘32’}
Player{name=‘kobe’, age=‘10000’}
Player{name=‘zimug’, age=‘27’}

方法二(stream)
  • 首先用stream()把list集合转换成流
  • 然后用collect及toCollection把流转换成集合
  • 然后剩下的就和方法一相同了
@Test
void testRemove6() {List<Player> newList = playerList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Player::getName))),ArrayList::new));newList.forEach(System.out::println);
}
方法三(不会打乱顺序)

这种方法也是笔者建议大家使用的一种方法,咋一看好像代码量更大了,但实际上这种方法是应用比较简单的方法。

Predicate(有人管这个叫断言,从英文的角度作为名词可以翻译为谓词,作为动词可以翻译为断言)。谓词就是用来修饰主语的,比如:喜欢唱歌的小鸟,喜欢唱歌就是谓词,用来限定主语的范围。所以我们这里是用来filter过滤的,也是用来限制主语范围的,所以我认为翻译为谓词更合适。随便吧,看你怎么觉得怎么理解合理、好记,你就怎么来。

  • 首先我们定义一个谓词Predicate用来过滤,过滤的条件是distinctByKey。谓词返回ture元素保留,返回false元素被过滤掉。
  • 当然我们的需求是过滤掉重复元素。我们去重逻辑是通过map的putIfAbsent实现的。putIfAbsent方法添加键值对,如果map集合中没有该key对应的值,则直接添加,并返回null,如果已经存在对应的值,则依旧为原来的值。
  • 如果putIfAbsent返回null表示添加数据成功(不重复),如果putIfAbsent返回value(value==null :false),则满足了distinctByKey谓词的条件元素被过滤掉。

这种方法虽然看上去代码量增大了,但是distinctByKey谓词方法只需要被定义一次,就可以无限复用。

@Test
void testRemove7() {List<Player> newList = new ArrayList<>();playerList.stream().filter(distinctByKey(p -> p.getName())) //filter保留true的值.forEach(newList::add);newList.forEach(System.out::println);
}static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {Map<Object,Boolean> seen = new ConcurrentHashMap<>();//putIfAbsent方法添加键值对,如果map集合中没有该key对应的值,则直接添加,并返回null,如果已经存在对应的值,则依旧为原来的值。//如果返回null表示添加数据成功(不重复),不重复(null==null :TRUE)return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

输出结果如下:三个zimug因为name重复,另外两个被去重。并且没有打乱List的原始顺序

Player{name=‘kobe’, age=‘10000’}
Player{name=‘james’, age=‘32’}
Player{name=‘curry’, age=‘30’}
Player{name=‘zimug’, age=‘27’}

方法四

第四种方法实际上不是新方法,上面的例子都是按某一个对象属性进行去重,如果我们想按照某几个元素进行去重,就需要对上面的三种方法进行改造。
我只改造其中一个,另外几个改造的原理是一样的,就是把多个比较属性加起来,作为一个String属性进行比较。

@Test
void testRemove8() {Set<Player> playerSet = new TreeSet<>(Comparator.comparing(o -> (o.getName() + "" + o.getAge())));playerSet.addAll(playerList);new ArrayList<>(playerSet).forEach(System.out::println);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90808.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90808.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/90808.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ADAS测试:如何用自动化手段提升VV效率

当前&#xff0c;ADAS 技术正在快速发展&#xff0c;从智能巡航控制到自动紧急制动等功能已逐渐成为汽车的标配。在不断提升驾驶辅助能力的同时&#xff0c;系统的可靠性也受到前所未有的重视。为了确保这些关键系统在各种工况下都能正常运行&#xff0c;验证与确认&#xff08…

互信息:理论框架、跨学科应用与前沿进展

1. 起源与核心定义 互信息&#xff08;Mutual Information, MI&#xff09;由克劳德香农&#xff08;Claude Shannon&#xff09; 在1948年开创性论文《A Mathematical Theory of Communication》中首次提出&#xff0c;该论文奠定了现代信息论的基础。互信息用于量化两个随机…

C++模板元编程从入门到精通

之前面试被问到什么是模板元编程&#xff0c;给我问懵了…… 一、什么是模板元编程&#xff08;TMP&#xff09; 模板元编程&#xff08;Template Metaprogramming, TMP&#xff09;是一种利用C模板在编译期执行计算和代码生成的编程范式。它本质上是“编写程序的程序”&#…

探秘CommonJS:Node.js模块化核心解析

CommonJS 是 JavaScript 的模块化规范&#xff0c;主要应用于 服务器端环境&#xff08;尤其是 Node.js&#xff09;&#xff0c;其核心目标是解决代码组织、依赖管理和作用域隔离问题 。以下是其核心要点&#xff1a;&#x1f527; 一、核心特性同步加载 模块通过 require() 同…

Windows 10 远程桌面(RDP)防暴力破解BAT脚本

0x01 设置5次失败后锁定账户30分钟 secpol.msc # 导航到: 安全设置 > 账户策略 > 账户锁定策略 0x02 复制保存到 BlockFailedRDP.ps1 <# .DESCRIPTION 此脚本分析Windows安全日志中的RDP登录失败事件(ID 4625)&#xff0c; 统计每个IP的失败次数&#xff0…

Chukonu 阅读笔记

Chukonu&#xff1a;一个将原生计算引擎集成到 Spark 中的全功能高性能大数据框架 摘要 Apache Spark 是一种广泛部署的大数据分析框架&#xff0c;它提供了诸如弹性、负载均衡和丰富的生态系统等吸引人的特性。然而&#xff0c;其性能仍有很大的改进空间。尽管用原生编程语言编…

51c视觉~3D~合集4

自己的原文哦~ https://blog.51cto.com/whaosoft/14084543 #VGGT-Long 首次将单目3D重建推向公里级极限&#xff01;南开、南大提出&#xff1a;分块、循环、对齐&#xff0c;开源 近年来&#xff0c;3D视觉基础模型&#xff08;Foundation Models&#xff09;在3D感…

实时云渲染将UE像素流嵌入业务系统,实现二维管理系统与数字孪生三维可视化程序的无缝交互

在数字孪生大屏可视化项目中&#xff0c;将实时云渲染技术嵌入业务系统已成为提升用户体验和工作效率的关键策略之一。将云渲染嵌入业务系统&#xff0c;用户可以在执行业务操作时实时看到云渲染画面的响应&#xff0c;同时对云渲染画面的操作也能立即反馈到业务系统中。这种无…

Apache POI 介绍与使用指南

文章框架一、Apache POI 概述定义&#xff1a;Java API操作Microsoft Office格式文件核心功能&#xff1a;读写Excel&#xff08;.xls, .xlsx&#xff09;操作Word、PowerPoint等文档优势&#xff1a;开源免费、跨平台、功能全面二、环境准备Maven依赖配置&#xff1a;<!-- …

Redis--哨兵机制详解

1. 哨兵机制简介Redis Sentinel&#xff08;哨兵&#xff09;是Redis的高可用性解决方案&#xff0c;它提供了监控、通知、自动故障转移和配置提供者等功能。Sentinel系统可以监控多个Redis主服务器及其从服务器&#xff0c;并在主服务器失效时自动进行故障转移&#xff0c;确保…

无人机机体结构设计要点难点分析

一、 设计要点1.轻量化&#xff1a;核心目标&#xff1a; 最大程度减轻结构重量&#xff0c;提升有效载荷能力、续航时间、飞行速度和机动性。实现手段&#xff1a; 选用高比强度/比刚度材料&#xff08;碳纤维复合材料、航空铝合金、钛合金、工程塑料&#xff09;、拓扑优化、…

AI时代的数据库革命:电科金仓的“融合+AI“战略解析

在人工智能时代的大变局下&#xff0c;数据库要走向何方&#xff1f; 7月15日&#xff0c;中国电科旗下金仓数据库以一场名为“融合进化 智领未来”的发布会&#xff0c;提出了自己的核心主张&#xff1a;真正的未来数据库&#xff0c;是“融合”为体&#xff0c;“AI”为用。电…

与deepseek的问答:dot net与Borland VCL的关系

Borland VCL与.NET/C#关系分析borland delphi如神一般地存在&#xff01;试分析.net、c#与Borland VCL的关系。Borland Delphi及其VCL&#xff08;Visual Component Library&#xff09;框架在软件开发史上确实具有传奇地位&#xff0c;尤其在Windows桌面应用开发领域。而随着.…

SAP在未启用负库存的情况下,库存却出现了负数-补充S4 1709 BUG

SAP在未启用负库存的情况下&#xff0c;库存却出现了负数-补充S4 1709 BUG共用物料合并发料&#xff1a;单行发料数量没有超过库存数量&#xff0c;但合计发料数量超过库存数量了&#xff0c;系统还是可以过账&#xff0c;没有任何提示&#xff0c;如下图所示&#xff1a;库存数…

SpringBoot项目中常见注解

RequiredArgsConstructor 注解 类上添加该注解&#xff0c;Lombok 会自动生成一个构造函数&#xff0c;用于注入 final 或 NonNull 修饰的字段 ConfigurationProperties注解 用于将配置文件中的属性注入到某个类的字段上 sky:jwt:admin-secret-key: itcastadmin-ttl: 7200000ad…

一键修复ipynb,Jupyter Notebook损坏文件

背景最近在写一个数据分析项目时&#xff0c;不幸遇到了 断电导致电脑重启 的突发情况。当我再次打开 Jupyter Notebook 文件&#xff08;.ipynb&#xff09;时&#xff0c;发现文件已经损坏&#xff0c;Jupyter 无法正常读取它&#xff0c;甚至有时直接报错&#xff1a;Unread…

React入门学习——指北指南(第三节)

React 组件 在前面的内容中,我们了解了 React 的基础知识和入门案例。本节将深入探讨 React 中最核心的概念之一 —— 组件。组件是构建 React 应用的基础,理解组件的工作原理和使用方法,对于掌握 React 开发至关重要。 什么是组件? 在 React 中,组件是具有独立功能和 …

容器化环境下的服务器性能瓶颈与优化策略

更多云服务器知识&#xff0c;尽在hostol.com在容器化环境中&#xff0c;性能优化并不是一个简单的“加硬件”或“增加资源”就能解决的问题。随着技术的进步&#xff0c;越来越多的公司选择使用容器技术&#xff08;如Docker、Kubernetes&#xff09;来提高应用的灵活性、可移…

GaussDB 数据库架构师修炼(八) 等待事件(2)-ASP报告分析

1 ASP报告简介ASP-Active Sesion Profile &#xff08;活跃会话档案信息&#xff09;&#xff0c;ASP每秒获取活跃会话事件&#xff0c;放到内存中&#xff0c;内存中的数据达阈值&#xff0c;会落盘gs_asp表中。ASP Report根据输入的时间段与slot个数&#xff0c;从内存和磁盘…

CentOS7 安装 Redis

在 CentOS 7 上配置 Redis 服务器需要完成安装、配置和服务管理。以下是详细步骤&#xff1a;安装 Redis安装依赖&#xff1a;yum install -y gcc tcl下载并解压 Redis&#xff1a;cd /usr/local/wget https://download.redis.io/releases/redis-6.2.6.tar.gztar -zxvf redis-6…