Java中List集合对象去重及按属性去重的8种方法

最近在写一些关于java基础的文章,但是我又不想按照教科书的方式去写知识点的文章,因为意义不大。基础知识太多了,如何将这些知识归纳总结,总结出优缺点或者是使用场景才是对知识的升华。所以我更想把java相关的基础知识进行穿针引线,进行整体上的总结。

  • 总结java中创建并写文件的5种方式
  • 总结java从文件中读取数据的6种方法
  • 总结java创建文件夹的4种方法及其优缺点
  • 总结java中删除文件或文件夹的7种方法
  • 总结java中文件拷贝剪切的5种方式

比如之前我已经写了上面的这些内容,如果对java基础知识总结系列感兴趣的同学可以关注我的博客(文末给出我的博客地址)。

一、本文梗概

这一篇文章我想写一下List集合元素去重的8种方法,实际上通过灵活的运用、排列组合不一定是8种,可能有18种方法。

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

为了在下文中进行测试内容讲解,我们先做一些初始化数据

public class ListRmDuplicate {
 private List<String> list;
 private List<Player> playerList;

 @BeforeEach
 public 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")); //注意这里名字和年龄重复

 }
}

Player对象就是一个普通的java对象,有两个成员变量name与age,实现了带参数构造函数、toString、equals和hashCode方法、以及GET/SET方法。

二、集合元素整体去重

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

第一种方法

是大家最容易想到的,先把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);
}

控制台打印结果如下:

去重后的集合: [kobe, james, zimug, curry]

第二种方法

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

@Test
void testRemove2() {
 List<String> newList = list.stream().distinct().collect(Collectors.toList());

 System.out.println( "去重后的集合: " + newList);
}

控制台打印结果如下:

去重后的集合: [kobe, james, curry, zimug]

第三种方法

这种方法利用了set.add(T),如果T元素已经存在集合中,就返回false。利用这个方法进行是否重复的数据判断,如果不重复就放入一个新的newList中,这个newList就是最终的去重结果

//三个集合类list、newList、set,能够保证顺序
@Test
void testRemove3() {

 Set<String> set = new HashSet<>();
 List<String> newList = new ArrayList<>();
 for (String str :list) {
 if(set.add(str)){ //重复的话返回false
 newList.add(str);
 }
 }
 System.out.println( "去重后的集合: " + newList);

}

控制台打印结果和第二种方法一致。

第四种方法

这种方法已经脱离了使用Set集合进行去重的思维,而是使用newList.contains(T)方法,在向新的List添加数据的时候判断这个数据是否已经存在,如果存在就不添加,从而达到去重的效果。

//优化 List、newList、set,能够保证顺序
@Test
void testRemove4() {

 List<String> newList = new ArrayList<>();
 for (String cd:list) {
 if(!newList.contains(cd)){ //主动判断是否包含重复元素
 newList.add(cd);
 }
 }
 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()把list集合转换成流
  • 然后用collect及toCollection把流转换成集合
  • 然后剩下的就和第一种方法一样了

前两步不是脱了裤子放屁么?看看就得了,实际应用意义不大,但是如果是为了学习Stream流的使用方法,搞出这么一个例子还是有可取之处的。

@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);
}

总结

到此这篇关于Java中List集合对象去重及按属性去重的8种方法的文章就介绍到这了,更多相关java list集合去重内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java集合中list的用法代码示例

    List接口是Collection接口的子接口,List有一个重要的实现类--ArrayList类,List中的元素是有序排列的而且可重复,所以被称为是序列. List可以精确的控制每个元素的插入位置,或删除某个位置元素,它的实现类ArrayList底层是由数组实现的. List中有增删改查的方法,我们可以通过例子演示: 我们通过对学生选课,来演示List中对课程增删改查的方法 /** * 课程类 * @author lenovo * */ public class KeCheng { publ

  • Java集合定义与用法实例总结【Set、List与Map】

    本文实例讲述了Java集合定义与用法.分享给大家供大家参考,具体如下: java集合大体可分为三类,分别是Set.List和Map,它们都继承了基类接口Collection,Collection接口定义了众多操作集合的基本方法,如下: 为了访问Collection集合,不得不去了解Iterator接口.该接口很简单,主要用于定义访问集合的方法,如下: 所以上述的三大类子集合必定都继承了上面2个接口.其中Set集合要求元素不重复,且内部无序,所以访问时只能根据元素值来访问:List内部为动态数组,

  • java使用lambda表达式对List集合进行操作技巧(JDK1.8)

    具体代码如下所示: import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; public class LambdaStudy { public static void main(String[] args) { //初始化list集合 List<String> list = new ArrayList&l

  • Java中对List去重 Stream去重的解决方法

    问题 当下互联网技术成熟,越来越多的趋向去中心化.分布式.流计算,使得很多以前在数据库侧做的事情放到了Java端.今天有人问道,如果数据库字段没有索引,那么应该如何根据该字段去重?大家都一致认为用Java来做,但怎么做呢? 解答 忽然想起以前写过list去重的文章,找出来一看.做法就是将list中对象的hashcode和equals方法重写,然后丢到HashSet里,然后取出来.这是最初刚学Java的时候像被字典一样背写出来的答案.就比如面试,面过号称做了3年Java的人,问Set和HashMa

  • java list去重操作实现方式

    Java中的List是可以包含重复元素的(hash code 和equals),那么对List进行去重操作有两种方式实现: 方案一:可以通过HashSet来实现,代码如下: 复制代码 代码如下: class Student { private String id; private String name; public Student(String id, String name) { super(); this.id = id; this.name = name; } @Override pu

  • 如何去除Java中List集合中的重复数据

    1.循环list中的所有元素然后删除重复 public class duplicatRemoval { public static List removeDuplicate(List list){ for(int i=0;i<list. size()-1;i++){ for(int j=list.size()-1;j>i;j--){ if(list. get(j). equals(list.get(i))){ list.remove(j); } } } return list; } } 总结:

  • java中List对象列表实现去重或取出及排序的方法

    前言 因为在面试的时候碰到几次list的去重和排序,觉着有必要给大家总结一下具体的方法,分享出来供大家学习参考,话不多说了,来一起看看下面介绍的一种做法: 一.list去重 1.1 实体类Student List<Student>容量10k以上,要求去重复.这里Student的重复标准是属性相同,因此需要重写equals和hashcode方法,不知道有几个可以手写出来. student的equals方法: public void equals(Object o){ if(this == o)

  • Java中List集合的遍历实例详解

     一.对List的遍历有三种方式   List<String> list = new ArrayList<String>(); list.add("testone"); list.add("testtwo"); ... 第一种: for(Iterator<String> it = list.iterator(); it.hasNext(); ) { .... } 这种方式在循环执行过程中会进行数据锁定,    性能稍差,    同

  • Java判断2个List集合是否相等(不考虑元素的顺序)

    现在有两个对象,他们的一个属性是list,很明显两个对象的list里面的对象,都是相等的,只是这2个list里面的顺序不一致,导致这2个对象被判断为不相等啦,这就是问题,现在要解决这个问题. 问题图如下: 可以看到这2个对象的呢个list属性里面数据,咱看起来是一样的,但是经过equals之后,返回的是false. 所以,需要自己重写equals方法和hashcode方法,这2个方法一般是一起重写的. 然后,问题的关键就在于,怎么判断2个list集合是否相等.不考虑顺序. 看代码: 先是这2个对

  • Java中List集合对象去重及按属性去重的8种方法

    最近在写一些关于java基础的文章,但是我又不想按照教科书的方式去写知识点的文章,因为意义不大.基础知识太多了,如何将这些知识归纳总结,总结出优缺点或者是使用场景才是对知识的升华.所以我更想把java相关的基础知识进行穿针引线,进行整体上的总结. 总结java中创建并写文件的5种方式 总结java从文件中读取数据的6种方法 总结java创建文件夹的4种方法及其优缺点 总结java中删除文件或文件夹的7种方法 总结java中文件拷贝剪切的5种方式 比如之前我已经写了上面的这些内容,如果对java基

  • 浅谈java中对集合对象list的几种循环访问

    java中对集合对象list的几种循环访问的总结如下  1 经典的for循环 public static void main(String[] args) { List<String> list = new ArrayList(); list.add("123"); list.add("java"); list.add("j2ee"); System.out.println("=========经典的for循环=======

  • JAVA中读取文件(二进制,字符)内容的几种方法总结

    JAVA中读取文件内容的方法有很多,比如按字节读取文件内容,按字符读取文件内容,按行读取文件内容,随机读取文件内容等方法,本文就以上方法的具体实现给出代码,需要的可以直接复制使用 public class ReadFromFile { /** * 以字节为单位读取文件,常用于读二进制文件,如图片.声音.影像等文件. */ public static void readFileByBytes(String fileName) { File file = new File(fileName); In

  • Java中List集合去重方法以及效率对比

    List集合相信大家在开发过程中几乎都会用到.有时候难免会遇到集合里的数据是重复的,需要进行去除.然而,去重方式有好几种方式,你用的是哪种方式呢?去重方式效率是否是最高效.最优的呢?今天就给大家讲解一下List集合去重的常见及常用的四种方式. 01 实现思路:使用两个for循环遍历集合所有元素,然后进行判断是否有相同元素,如果有,则去除.这种方式是大部分最先想到的,也是最简单的实现方式.其中,这种方式可以保证List集合原来的顺序不变. 代码实现: /** * notes:使用两个for循环实现

  • Java中Map集合中的Entry对象用法

    Entry: 键值对 对象. 在Map类设计是,提供了一个嵌套接口(static修饰的接口):Entry.Entry将键值对的对应关系封装成了对象,即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值. Entry为什么是静态的? Entry是Map接口中提供的一个静态内部嵌套接口,修饰为静态可以通过类名调用. Map集合遍历键值对的方式: Set<Map.Entry<K,V>> entrySet(); //返回此映射中包含的映射

  • Java中List集合去除重复数据的方法汇总

    List集合概述 List集合是一个元素有序(每个元素都有对应的顺序索引,第一个元素索引为0).且可重复的集合. List集合常用方法 List是Collection接口的子接口,拥有Collection所有方法外,还有一些对索引操作的方法. void add(int index, E element);:将元素element插入到List集合的index处: boolean addAll(int index, Collection<? extends E> c);:将集合c所有的元素都插入到

  • java中避免集合死链调用详情

    目录 1. 前言 2. 场景 3. 环境 3.1 开发环境准备 3.2 数据准备 3.2.1 Mysql数据库表及数据 3.2.2 redis库数据 4. 解决方式 5.完整代码 5.1 Model 5.2 Controller 5.3 Service 5.4 Service 实现类 6.测试 1. 前言  开发过程中, 一些集合 的变动会触发任务去 改变 其他的集合 ,为了保障任务的正确执行,应避免出现死循环调用,即对 集合之间的影响关系 进行一些限制.怕日后遗忘,特在此记录. 2. 场景 A

  • javascript实现Java中的Map对象功能的实例详解

    javascript  自定义对象实现Java中的Map对象功能 Java中有集合,Map等对象存储工具类,这些对象使用简易,但是在JavaScript中,你只能使用Array对象. 这里我创建一个自定义对象,这个对象内包含一个数组来存储数据,数据对象是一个Key,可以实际存储的内容! 这里Key,你要使用String类型,和Java一样,你可以进行一些增加,删除,修改,获得的操作. 使用很简单,我先把工具类给大家看下: /** * @version 1.0 * @author cuisuqia

  • 深入剖析java中的集合框架

    解析:如果并不知道程序运行时会需要多少对象,或者需要更复杂方式存储对象,那么可以使用Java集合框架. 如果启用集合的删除方法,那么集合中所有元素的索引会自动维护. 集合完全弥补了数组的缺陷. 02.集合框架的内容 集合框架都包含三大块内容:对外的接口,接口的实现和对集合运算的算法 01.接口:表示集合的抽象数据类型 02.实现:集合框架中接口的具体实现 03.算法:在一个实现了某个集合框架的接口的对象身上完成某种有用的计算方法 java集合框架简图: 01.Collection接口存在储存一组

随机推荐