千家信息网

java List集合对象去重及按属性去重的方法有哪些

发表于:2025-02-04 作者:千家信息网编辑
千家信息网最后更新 2025年02月04日,本篇内容介绍了"java List集合对象去重及按属性去重的方法有哪些"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读
千家信息网最后更新 2025年02月04日java List集合对象去重及按属性去重的方法有哪些

本篇内容介绍了"java List集合对象去重及按属性去重的方法有哪些"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一、本文梗概

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

  • 对象元素整体去重的4种方法

  • 按照对象属性去重的4种方法

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

public class ListRmDuplicate {  private List list;  private List 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去重是一样的。

第一种方法

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

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

控制台打印结果如下:

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

第二种方法

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

[@Test](https://my.oschina.net/azibug)void testRemove2()  {  List 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](https://my.oschina.net/azibug)void testRemove3()  {  Set set = new HashSet<>();  List 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](https://my.oschina.net/azibug)void testRemove4() {  List 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)

@Testvoid testRemove5() {  //Set playerSet = new TreeSet<>((o1, o2) -> o1.getName().compareTo(o2.getName()));  Set 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流的使用方法,搞出这么一个例子还是有可取之处的。

@Testvoid testRemove6() {  List 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谓词方法只需要被定义一次,就可以无限复用。

@Testvoid testRemove7() {  List newList = new ArrayList<>();  playerList.stream().filter(distinctByKey(p -> p.getName()))  //filter保留true的值          .forEach(newList::add);  newList.forEach(System.out::println);}static  Predicate distinctByKey(Function keyExtractor) {  Map 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属性进行比较。

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

"java List集合对象去重及按属性去重的方法有哪些"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

方法 元素 对象 属性 数据 结果 谓词 实际 就是 控制台 顺序 排序 控制 接口 整体 输出 三个 两个 主语 代码 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 华容安卓软件开发 单位网络安全日常维护细则 搭建小程序需要多大服务器 青岛负责网络安全张 工控网络安全政策与形势分析 数据库结构 展示系统 数据库如何备份数据库 乌兰浩特短期云计算网络安全 苏州人工智能软件开发要多少钱 东宁手机软件开发 电脑怎么开一个服务器 日本网络安全专业怎样 java数据库连接不释放 网络安全保密工作笔记 北京市软件开发公司 去年成立 福州职业技术学院网络技术 网络安全事故限制上市 免费阅读公益性社会学刊的数据库 服务器生产质量管理体系 java游戏软件开发培训班 创建的数据库怎么打开吗 常州通用软件开发流程 信息系统网络安全案例分析介绍 全国普法关于网络安全 洛奇与服务器连接失败 服务器开机起不来一般是什么原因 网络安全宣传周罗湖 戴尔机架式服务器r440 文登软件开发有哪些 奉化财务软件开发报价
0