欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中List集合對象去重及按屬性去重的8種方法

 更新時間:2020年09月08日 09:16:52   作者:字母哥博客  
這篇文章主要介紹了Java中List集合對象去重及按屬性去重的8種方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一地的參考借鑒價值,需要的朋友可以參考下

最近在寫一些關(guān)于java基礎的文章,但是我又不想按照教科書的方式去寫知識點的文章,因為意義不大?;A知識太多了,如何將這些知識歸納總結(jié),總結(jié)出優(yōu)缺點或者是使用場景才是對知識的升華。所以我更想把java相關(guān)的基礎知識進行穿針引線,進行整體上的總結(jié)。

  • 總結(jié)java中創(chuàng)建并寫文件的5種方式
  • 總結(jié)java從文件中讀取數(shù)據(jù)的6種方法
  • 總結(jié)java創(chuàng)建文件夾的4種方法及其優(yōu)缺點
  • 總結(jié)java中刪除文件或文件夾的7種方法
  • 總結(jié)java中文件拷貝剪切的5種方式

比如之前我已經(jīng)寫了上面的這些內(nèi)容,如果對java基礎知識總結(jié)系列感興趣的同學可以關(guān)注我的博客(文末給出我的博客地址)。

一、本文梗概

這一篇文章我想寫一下List集合元素去重的8種方法,實際上通過靈活的運用、排列組合不一定是8種,可能有18種方法。

  • 對象元素整體去重的4種方法
  • 按照對象屬性去重的4種方法

為了在下文中進行測試內(nèi)容講解,我們先做一些初始化數(shù)據(jù)

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,實現(xiàn)了帶參數(shù)構(gòu)造函數(shù)、toString、equals和hashCode方法、以及GET/SET方法。

二、集合元素整體去重

下文中四種方法對List中的String類型以集合元素對象為單位整體去重。如果你的List放入的是Object對象,需要你去實現(xiàn)對象的equals和hashCode方法,去重的代碼實現(xiàn)方法和List<String>去重是一樣的。

第一種方法

是大家最容易想到的,先把List數(shù)據(jù)放入Set,因為Set數(shù)據(jù)結(jié)構(gòu)本身具有去重的功能,所以再將SET轉(zhuǎn)為List之后就是去重之后的結(jié)果。這種方法在去重之后會改變原有的List元素順序,因為HashSet本身是無序的,而TreeSet排序也不是List種元素的原有順序。

@Test
void testRemove1() {
 /*Set<String> set = new HashSet<>(list);
 List<String> newList = new ArrayList<>(set);*/

 //去重并排序的方法(如果是字符串,按字母表排序。如果是對象,按Comparable接口實現(xiàn)排序)
 //List<String> newList = new ArrayList<>(new TreeSet<>(list));

 //簡寫的方法
 List<String> newList = new ArrayList<>(new HashSet<>(list));

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

控制臺打印結(jié)果如下:

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

第二種方法

使用就比較簡單,先用stream方法將集合轉(zhuǎn)換成流,然后distinct去重,最后在將Stream流collect收集為List。

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

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

控制臺打印結(jié)果如下:

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

第三種方法

這種方法利用了set.add(T),如果T元素已經(jīng)存在集合中,就返回false。利用這個方法進行是否重復的數(shù)據(jù)判斷,如果不重復就放入一個新的newList中,這個newList就是最終的去重結(jié)果

//三個集合類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);

}

控制臺打印結(jié)果和第二種方法一致。

第四種方法

這種方法已經(jīng)脫離了使用Set集合進行去重的思維,而是使用newList.contains(T)方法,在向新的List添加數(shù)據(jù)的時候判斷這個數(shù)據(jù)是否已經(jīng)存在,如果存在就不添加,從而達到去重的效果。

//優(yōu)化 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);

}

控制臺打印結(jié)果和第二種方法一致。

三、按照集合元素對象屬性去重

其實在實際的工作中,按照集合元素對象整體去重的應用的還比較少,更多的是要求我們按照元素對象的某些屬性進行去重。
看到這里請大家回頭去看一下上文中,構(gòu)造的初始化數(shù)據(jù)playerList,特別注意其中的一些重復元素,以及成員變量重復。

第一種方法

為TreeSet實現(xiàn)Comparator接口,如果我們希望按照Player的name屬性進行去重,就去在Comparator接口中比較name。下文中寫了兩種實現(xiàn)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());
 });*/
 //將去重之后的結(jié)果打印出來
 new ArrayList<>(playerSet).forEach(System.out::println);
}

輸出結(jié)果如下:三個zimug因為name重復,另外兩個被去重。但是因為使用到了TreeSet,list中元素被重新排序。

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

第二種方法

這種方法是網(wǎng)上很多的文章中用來顯示自己很牛的方法,但是在筆者看來有點脫了褲子放屁,多此一舉。既然大家都說有這種方法,我不寫好像我不牛一樣。我為什么說這種方法是“脫了褲子放屁”?

  • 首先用stream()把list集合轉(zhuǎn)換成流
  • 然后用collect及toCollection把流轉(zhuǎn)換成集合
  • 然后剩下的就和第一種方法一樣了

前兩步不是脫了褲子放屁么?看看就得了,實際應用意義不大,但是如果是為了學習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元素被過濾掉。
  • 當然我們的需求是過濾掉重復元素。我們?nèi)ブ剡壿嬍峭ㄟ^map的putIfAbsent實現(xiàn)的。putIfAbsent方法添加鍵值對,如果map集合中沒有該key對應的值,則直接添加,并返回null,如果已經(jīng)存在對應的值,則依舊為原來的值。
  • 如果putIfAbsent返回null表示添加數(shù)據(jù)成功(不重復),如果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,如果已經(jīng)存在對應的值,則依舊為原來的值。
 //如果返回null表示添加數(shù)據(jù)成功(不重復),不重復(null==null :TRUE)
 return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

輸出結(jié)果如下:三個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);
}

總結(jié)

到此這篇關(guān)于Java中List集合對象去重及按屬性去重的8種方法的文章就介紹到這了,更多相關(guān)java list集合去重內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解基于java的Socket聊天程序——初始設計(附demo)

    詳解基于java的Socket聊天程序——初始設計(附demo)

    本篇文章主要介紹了Socket聊天程序——初始設計(附demo),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • startJVM錯誤Unable to load native library: libjvm.so解決方法

    startJVM錯誤Unable to load native library: libjvm.so解決方法

    這篇文章主要介紹了startJVM錯誤Unable to load native library: libjvm.so解決方法,需要的朋友可以參考下
    2014-07-07
  • Java1.8中LocalDate方法使用總結(jié)

    Java1.8中LocalDate方法使用總結(jié)

    LocalDate是Java8中的一個日期類,用于表示年月日,它是不可變的,線程安全的,下面這篇文章主要給大家介紹了關(guān)于Java1.8中LocalDate方法使用的相關(guān)資料,需要的朋友可以參考下
    2024-03-03
  • 三種java編程方法實現(xiàn)斐波那契數(shù)列

    三種java編程方法實現(xiàn)斐波那契數(shù)列

    這篇文章主要為大家詳細介紹了三種java編程方法實現(xiàn)斐波那契數(shù)列,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • springboot登陸頁面圖片驗證碼簡單的web項目實現(xiàn)

    springboot登陸頁面圖片驗證碼簡單的web項目實現(xiàn)

    這篇文章主要介紹了springboot登陸頁面圖片驗證碼簡單的web項目實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-04-04
  • SpringBoot自定義線程池,執(zhí)行定時任務方式

    SpringBoot自定義線程池,執(zhí)行定時任務方式

    這篇文章主要介紹了SpringBoot自定義線程池,執(zhí)行定時任務方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • Spring Boot和Hazelcast使用詳解

    Spring Boot和Hazelcast使用詳解

    這篇文章主要介紹了Spring Boot和Hazelcast使用詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • Mybatis 自定義類型處理器示例詳解

    Mybatis 自定義類型處理器示例詳解

    在某些情況下我們需要對類型做處理,例如數(shù)據(jù)存儲的是Long,程序里是BigDecimal,那么我們出庫入庫都需要做處理,此時就可以使用類型處理器,本文通過示例給大家介紹Mybatis 自定義類型處理器的相關(guān)知識,感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • SWT(JFace)小制作 BugTracker

    SWT(JFace)小制作 BugTracker

    SWT(JFace)小制作 BugTracker
    2009-06-06
  • SpringBoot3安全管理操作方法

    SpringBoot3安全管理操作方法

    這篇文章主要介紹了SpringBoot3安全管理,在實際開發(fā)中,最常用的是登錄驗證和權(quán)限體系兩大功能,在登錄時完成身份的驗證,加載相關(guān)信息和角色權(quán)限,在訪問其他系統(tǒng)資源時,進行權(quán)限的驗證,保護系統(tǒng)的安全,文中有詳細的操作步驟,需要的朋友可以參考下
    2023-08-08

最新評論