List 去重的 6 种方法,这个方法最完美!

1a4ba5373137e0469ef826fa66180b6d.png

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

在日常的业务开发中,偶尔会遇到需要将 List 集合中的重复数据去除掉的场景。

这个时候可能有同学会问:为什么不直接使用 Set 或者 LinkedHashSet 呢?这样不就没有重复数据的问题了嘛?

不得不说,能提这个问题的同学很机智,一眼就看到了问题的本质。

但是,在实际的业务开发中遇到的情况会更复杂。比如,List 集合可能是历史遗留问题,也有可能是调用接口返回的类型限制,只能使用 List 接收,又或者是代码写了一半,在做多个集合合并的时候才发现了这个问题,总之造成问题的原因有很多种,这里就不一一列举了。

当发现这个问题之后,如果可以通过改造原有代码,把原来的 List 类型替换成 Set 类型,那就可以直接修改集合的类型即可。但如果压根就修改不了,或者是修改的成本太大,那接下来这 6 种去重的方法,将帮你将解决问题。

前置知识

正式开始之前,先来搞懂两组概念:无序集合和有序集合 & 无序和有序。因为接下来的方法实现中,会反复提及这两组概念,所以有必要在正式开始之前,先把它们搞清楚。

无序集合

无序集合是指,数据读取的顺序和数据插入的顺序是不一致的。

例如,插入集合的顺序是:1、5、3、7,而集合的读取顺序竟然是:1、3、5、7。

有序集合

有序集合的概念和无序集合的概念正好相反,它是指集合的读取顺序和插入顺序是一致的。

例如,插入数据的顺序是:1、5、3、7,那么读取的顺序也是:1、5、3、7。

有序和无序

通过上面的无序集合和有序集合,我们可以得出有序和无序的概念。有序指的是数据的排列顺序和读取顺序符合我们的预期就叫做有序。而无序指的是数据的排列顺序和读取顺序不符合我们的预期就叫做无序。

PS:如果对于有序和无序的概念不是很清楚也没关系,通过下面的事例,我们可以进一步的理解它们的含义。

方法1:contains判断去重(有序)

要进行数据去重,我们首先想到的是新建一个集合,然后循环原来的集合,每次循环判断原集合中的循环项,如果当前循环的数据,没有在新集合中存在就插入,已经存在了就舍弃,这样当循环执行完,我们就得到了一个没有重复元素的集合了,实现代码如下:

public class ListDistinctExample {public static void main(String[] args) {List<Integer> list = new ArrayList<Integer>() {{add(1);add(3);add(5);add(2);add(1);add(3);add(7);add(2);}};System.out.println("原集合:" + list);method(list);}/*** 自定义去重* @param list*/public static void method(List<Integer> list) {// 新集合List<Integer> newList = new ArrayList<>(list.size());list.forEach(i -> {if (!newList.contains(i)) { // 如果新集合中不存在则插入newList.add(i);}});System.out.println("去重集合:" + newList);}
}

以上程序执行的结果,如下所示:d3612b93052a1bbcbb7a28c1ba748e6d.png此方法的优点的:理解起来比较简单,并且最终得到的集合也是有序的,这里的有序指的是新集合的排列顺序和原集合的顺序是一致的;但缺点是实现代码有点多,不够简洁优雅。

方法2:迭代器去重(无序)

自定义 List 去重,除了上面的新建集合之外,我们也可以使用迭代器循环判断每一项数据,如果当前循环的数据,在集合中存在两份或两份以上,就将当前的元素删除掉,这样循环完之后,也可以得到一个没有重复数据的集合,实现代码如下:

public class ListDistinctExample {public static void main(String[] args) {List<Integer> list = new ArrayList<Integer>() {{add(1);add(3);add(5);add(2);add(1);add(3);add(7);add(2);}};System.out.println("原集合:" + list);method_1(list);}/*** 使用迭代器去重* @param list*/public static void method_1(List<Integer> list) {Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()) {// 获取循环的值Integer item = iterator.next();// 如果存在两个相同的值if (list.indexOf(item) != list.lastIndexOf(item)) {// 移除最后那个相同的值iterator.remove();}}System.out.println("去重集合:" + list);}
}

以上程序执行的结果,如下所示:f9c1022c8db2ecdef6c6281cb5cd7058.png此方法的实现比上一种方法的实现代码要少一些,并且不需要新建集合,但此方法得到的新集合是无序的,也就是新集合的排列顺序和原集合不一致,因此也不是最优的解决方案。

方法3:HashSet去重(无序)

我们知道 HashSet 天生具备“去重”的特性,那我们只需要将 List 集合转换成 HashSet 集合就可以了,实现代码如下:

public class ListDistinctExample {public static void main(String[] args) {List<Integer> list = new ArrayList<Integer>() {{add(1);add(3);add(5);add(2);add(1);add(3);add(7);add(2);}};System.out.println("原集合:" + list);method_2(list);}/*** 使用 HashSet 去重* @param list*/public static void method_2(List<Integer> list) {HashSet<Integer> set = new HashSet<>(list);System.out.println("去重集合:" + set);}
}

以上程序执行的结果,如下所示:12539c4b64e65512e1ea40b2ba9e90a4.png此方法的实现代码较为简洁,但缺点是 HashSet 会自动排序,这样新集合的数据排序就和原集合不一致了,如果对集合的顺序有要求,那么此方法也不能满足当前需求。

方法4:LinkedHashSet去重(有序)

既然 HashSet 会自动排序不能满足需求,那就使用 LinkedHashSet,它既能去重又能保证集合的顺序,实现代码如下:

public class ListDistinctExample {public static void main(String[] args) {List<Integer> list = new ArrayList<Integer>() {{add(1);add(3);add(5);add(2);add(1);add(3);add(7);add(2);}};System.out.println("原集合:" + list);method_3(list);}/*** 使用 LinkedHashSet 去重* @param list*/public static void method_3(List<Integer> list) {LinkedHashSet<Integer> set = new LinkedHashSet<>(list);System.out.println("去重集合:" + set);}
}

以上程序执行的结果,如下所示:5b39dc0d6b5a2403377c21e16b3017f2.png从上述代码和执行结果可以看出,LinkedHashSet 是到目前为止,实现比较简单,且最终生成的新集合与原集合顺序保持一致的实现方法,是我们可以考虑使用的一种去重方法。

方法5:TreeSet去重(无序)

除了以上的 Set 集合之外,我们还可以使用 TreeSet 集合来实现去重功能,实现代码如下:

public class ListDistinctExample {public static void main(String[] args) {List<Integer> list = new ArrayList<Integer>() {{add(1);add(3);add(5);add(2);add(1);add(3);add(7);add(2);}};System.out.println("原集合:" + list);method_4(list);}/*** 使用 TreeSet 去重(无序)* @param list*/public static void method_4(List<Integer> list) {TreeSet<Integer> set = new TreeSet<>(list);System.out.println("去重集合:" + set);}
}

以上程序执行的结果,如下所示:4cfada44431cd2f1dcf3575847315295.png比较遗憾的是,TreeSet 虽然实现起来也比较简单,但它有着和 HashSet 一样的问题,会自动排序,因此也不能满足我们的需求。

方法6:Stream去重(有序)

JDK 8 为我们带来了一个非常实用的方法 Stream,使用它可以实现很多功能,比如下面的去重功能:

public class ListDistinctExample {public static void main(String[] args) {List<Integer> list = new ArrayList<Integer>() {{add(1);add(3);add(5);add(2);add(1);add(3);add(7);add(2);}};System.out.println("原集合:" + list);method_5(list);}/*** 使用 Stream 去重* @param list*/public static void method_5(List<Integer> list) {list = list.stream().distinct().collect(Collectors.toList());System.out.println("去重集合:" + list);}
}

以上程序执行的结果,如下所示:f78b0cedc873d4283ba3f137742685ea.pngStream 实现去重功能和其他方法不同的是,它不用新创建集合,使用自身接收一个去重的结果就可以了,并且实现代码也很简洁,并且去重后的集合顺序也和原集合的顺序保持一致,是我们最优先考虑的去重方法。

总结

本文我们介绍了 6 种集合去重的方法,其中实现最简洁,且去重之后的顺序能和原集合保持一致的实现方法,只有两种:LinkedHashSet 去重和 Stream 去重,而后一种去重方法无需借助新集合,是我们优先考虑的去重方法。

46c12fc2307f8213aaac73f76a0ddcc2.gif

往期推荐

d0561b00f321c01b75479f9db1c4b62a.png

Java 中 List 分片的 5 种方法!


5f317053c999d21a95090f843cc6051a.png

数组转List的3种方法和使用对比!


0b1a1640626259db5fc72fab6235766c.png

优雅统计代码耗时的4种方法!


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

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

相关文章

Mongodb -(3) replica set+sharding

分片集搭建---何旭东目录分片集搭建...................................................................................................................... 1生态系统...............................................................................................…

electron 菜单栏_如何在Electron JS中添加任务栏图标菜单?

electron 菜单栏If you are new here, please consider checking out my recent articles on Electron JS including Tray Icons. 如果您是新来的&#xff0c;请考虑查看我最近关于Electron JS的文章&#xff0c; 包括托盘图标 。 In this tutorial, we will set up 2 menu it…

【逆强化学习-0】Introduction

文章目录专栏传送门0.引言1.逆强化学习发展历程2.需要准备的专栏传送门 0.简介 1.学徒学习 2.最大熵学习 0.引言 \qquad相比于深度学习&#xff0c;国内强化学习的教程并不是特别多&#xff0c;而相比强化学习&#xff0c;逆强化学习的教程可谓是少之又少。而本人想将整理到的资…

不知道Mysql排序的特性,加班到12点,认了认了!

小弟新写了一个功能&#xff0c;自测和测试环境测试都没问题&#xff0c;但在生产环境会出现偶发问题。于是&#xff0c;加班到12点一直排查问题&#xff0c;终于定位了的问题原因&#xff1a;Mysql Limit查询优化导致。现抽象出问题模型及解决方案&#xff0c;分析给大家&…

js中==与===的区别

2019独角兽企业重金招聘Python工程师标准>>> 1、对于string,number等基础类型&#xff0c;和是有区别的 1&#xff09;不同类型间比较&#xff0c;之比较“转化成同一类型后的值”看“值”是否相等&#xff0c;如果类型不同&#xff0c;其结果就是不等 2&#xff09…

c语言中memcpy函数_带有示例的C中的memcpy()函数

c语言中memcpy函数memcpy()函数 (memcpy() function) memcpy() is a library function, which is declared in the “string.h” header file - it is used to copy a block of memory from one location to another (it can also be considered as to copy a string to anothe…

【逆强化学习-1】学徒学习(Apprenticeship Learning)

文章目录0.引言1.算法原理2.仿真环境3.运行4.补充&#xff08;学徒学习深度Q网络&#xff09;本文为逆强化学习系列第1篇&#xff0c;没有看过逆强化学习介绍的那篇的朋友&#xff0c;可以看一下&#xff1a;Inverse Reinforcement Learning-Introduction 传送门 0.引言 \qquad…

面试官:HashMap有几种遍历方法?推荐使用哪种?

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;HashMap 的遍历方法有很多种&#xff0c;不同的 JDK 版本有不同的写法&#xff0c;其中 JDK 8 就提供了 3 种 HashMa…

HTML 5 input placeholder 属性

<input placeholder"请先选择组织" type"text" value"" </input>placeholder 属性提供可描述输入字段预期值的提示信息&#xff08;hint&#xff09;。 该提示会在输入字段为空时显示&#xff0c;并会在字段获得焦点时消失。 注释&…

【逆强化学习-2】最大熵学习(Maximum Entropy Learning)

文章目录0.引言1.算法原理2.仿真0.引言 \qquad本文是逆强化学习系列的第2篇&#xff0c;其余博客传送门如下&#xff1a; 逆强化学习0-Introduction 逆强化学习1-学徒学习 \qquad最大熵学习是2008年出现的方法&#xff0c;原论文&#xff08;链接见【逆强化学习0】的博客&#…

uselocale_Java扫描仪useLocale()方法与示例

uselocale扫描器类useLocale()方法 (Scanner Class useLocale() method) useLocale() method is available in java.util package. useLocale()方法在java.util包中可用。 useLocale() method is used to use this Scanner locale to the given locale (lo). useLocale()方法用…

面试官又整新活,居然问我for循环用i++和++i哪个效率高?

前几天&#xff0c;一个小伙伴告诉我&#xff0c;他在面试的时候被面试官问了这么一个问题&#xff1a;在for循环中&#xff0c;到底应该用 i 还是 i &#xff1f;听到这&#xff0c;我感觉这面试官确实有点不按套路出牌了&#xff0c;放着好好的八股文不问&#xff0c;净整些幺…

UVa 988 - Many Paths, One Destination

称号&#xff1a;生命是非常多的选择。现在给你一些选择&#xff08;0~n-1&#xff09;&#xff0c;和其他选项后&#xff0c;分支数每一次选择&#xff0c;选择共求。 分析&#xff1a;dp&#xff0c;图论。假设一个状态也许是选择的数量0一个是&#xff0c;代表死亡&#xff…

Java PrintWriter close()方法与示例

PrintWriter类close()方法 (PrintWriter Class close() method) close() method is available in java.io package. close()方法在java.io包中可用。 close() method is used to close this stream and free all system resources linked with the stream. close()方法用于关闭…

pipedreader_Java PipedReader ready()方法与示例

pipedreaderPipedReader类ready()方法 (PipedReader Class ready() method) ready() method is available in java.io package. ready()方法在java.io包中可用。 ready() method is used to check whether this PipedReader stream is ready to be read or not. ready()方法用…

面试官:如何实现 List 集合去重?

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;本文已收录《Java常见面试题》系列&#xff0c;开源地址&#xff1a;https://gitee.com/mydb/interviewList 去重指的…

Windows重装Anaconda3失败解决方案【重装失败10来次首次成功的案例!】

文章目录0.环境1.原因2.解决方案0.环境 Win10 Anaconda3 2018版 python 3.7.1 注意&#xff01;此种情况只会在windows上发生&#xff0c;因为在linux上你只需要删除anaconda3整个文件夹&#xff0c;重新安装一定会成功&#xff01; 1.原因 Anaconda肯定是没有成功安装的&am…

java写入文件的几种方法分享

转自&#xff1a;http://www.jb51.net/article/47062.htm 一&#xff0c;FileWritter写入文件 FileWritter, 字符流写入字符到文件。默认情况下&#xff0c;它会使用新的内容取代所有现有的内容&#xff0c;然而&#xff0c;当指定一个true &#xff08;布尔&#xff09;值作为…

python读取pcd点云/转numpy(python2+python3,非ROS环境)

0.引言 \qquadROS的PCL库支持python读取点云&#xff0c;ROS1关联的是python2&#xff08;2.7&#xff09;&#xff0c;ROS2关联的是python3&#xff08;>3.5&#xff09;&#xff0c;但这对于windows的用户和没装ROS的ubuntu用户似乎不够友好。下面就介绍两种不需要ros的方…

Java中List排序的3种方法!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在某些特殊的场景下&#xff0c;我们需要在 Java 程序中对 List 集合进行排序操作。比如从第三方接口中获取所有用户的列表&…