自定義類集合去重

上文:《面試被問到 HashMap 有這一文就夠了!》

Set 中的元素保證唯一性,因此可以藉助 Set 集合的這個特性對其他集合中的元素進行去重操作:

public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,2,3,4,5,5,6));
        Set<Integer> set = new HashSet<Integer>(list);
        System.out.println(set);
} //[1, 2, 3, 4, 5, 6]

這樣可以輕鬆地對 int(Integer)、double(Double)、String。。。等等基本類型及其包裝類的集合進行去重,而不必去顯示的編寫一個 for 循環。

在日常編碼中經常遇到自定義類集合去重的需求:

@AllArgsConstructor
public class BasicInfo {
    private Long basicInfoId;
    private String empId;
    private String name;
    private Integer gender;
    private String company;
     @Override
    public String toString() {。。。}
    。。。
}

假如因爲多表連接時因爲某些表中數據的特性,不可避免的會導致 list 中存在相同的元素:

//根據上述類的定義,不難看出數據庫中表的部分定義
//loadBasicInfo()方法會查出 BasicInfo 類實例的集合
List<BasicInfo> list = paMapper.loadBasicInfo(xxx,xxx,xxx,xxx);

PS:假設每條數據的 basicInfoId 和 empId 都可以唯一的標識一條數據。(爲了方便舉例,應用時結合實際數據特點)

TreeSet 去重

這時最"笨"的方法就是用 for+contains 的方法去遍歷去重;其實我們依然可以使用 Set 集合的特性去對這個自定義類集合去重。原理如下(在前文的末尾有詳細介紹):

  • Comparable 接口出自 java.lang 包下,接口下只有一個 compareTo(T o)抽象方法,如果希望 TreeMap/TreeSet 插入元素時希望採用自定義排序,或者希望自定義 Set 去重規則,可以讓插入對象實現這個接口重寫方法:

    public int compareTo(T o);
    
  • Comparator 是個函數式接口出自 java.util 包下,這個接口下方法有二十多個方法,其中有一個抽象方法 compare(T o1, T o2),其常用方式是調用帶參數的Collections.sort()時傳一個 Comparator 的匿名類,支持採用 Lambda 的方式實現:

    int compare(T o1, T o2);
    

當我們需要對一個集合進行自定義排序或者自定義 Set 去重規則時,可以重寫 compare(T o1, T o2)或者 compareTo(T o)

例如:當我們需要對某一個集合實現兩種自定義排序的時候,比如對 Student 對象元素中的姓名採用一種自定義排序方式、學校名採用另一種自定義排序方式的需求,可以重寫 compareTo(T o)方法實現一種、再實現 Comparable 接口實現另一種方法,也可以用兩個 Comparator 分別重寫compareTo(T o)方法進行實現,第二個方案可以使用兩個帶參數的 Collections.sort()實現。

直接看例子:

public static void main(String[] args) {
        //模擬數據
        BasicInfo info1 = new BasicInfo(111L, "Key001", "張三", 1, "阿狸");
        BasicInfo info2 = new BasicInfo(112L, "Key002", "李四", 0, "百毒");
        BasicInfo info3 = new BasicInfo(111L, "Key001", "張三", 1, "阿狸");
        BasicInfo info4 = new BasicInfo(111L, "Key001", "張三", 1, "阿狸");
        BasicInfo info5 = new BasicInfo(113L, "Key003", "王五", 1, "疼訊");
        BasicInfo info6 = new BasicInfo(112L, "Key002", "李四", 0, "百毒");
        List<BasicInfo> list = new ArrayList<BasicInfo>(Arrays.asList(info1,info2,info3,info4,info5,info6));
        //去重
        Comparator<BasicInfo> comparator = new Comparator<BasicInfo>() {
            public int compare(BasicInfo e1, BasicInfo e2) {
                return e1.getBasicInfoId().compareTo(e2.getBasicInfoId());
            }
        };
        Set<BasicInfo> set = new TreeSet<BasicInfo>(comparator);
        set.addAll(list);
        for (BasicInfo info : set) {
            System.out.println(info);
        }
    }
//輸出:
//BasicInfo{basicInfoId=111, empId='Key001', name='張三', gender=1, company='阿狸'}
//BasicInfo{basicInfoId=112, empId='Key002', name='李四', gender=0, company='百毒'}
//BasicInfo{basicInfoId=113, empId='Key003', name='王五', gender=1, company='疼訊'}

HashSet去重

HashSet 的數據結構是直接 new 了一個 HashMap,由於 HashMap 的 Key 是唯一的,HashSet 正是利用這一性質進行去重。(轉化爲自定義類做 HashMap 的 Key 的問題)

HashSet 在元素插入時,先調用 HashMap 的 hash()方法計算插入對象的 hashcode值去得到對象加入的位置,同時會和集合中其他的對象的 hashcode 值進行比較,如果沒有相同的 hashcode 值則說明對象沒重複;如果 hashcode 值重複,這是會調用對象的equals()方法來檢查 hashcode 相同的對象是否真的相同,最終相同則插入失敗,否則成功。

前文有詳細介紹:《面試被問到 HashMap 有這一文就夠了!》

所以利用 HashSet 去重(當自定義類作爲 HashMap 的 Key 的時候)需要去重寫自定義類的 hashCode() 方法和 equals() 方法。我們經常用 String 作爲 HashMap 的 Key 並且沒有重寫其方法,是因爲 String 在設計是已經重寫了 hashCode() 和 equals() 方法 。

去重示例:

@AllArgsConstructor
public class BasicInfo {
    private Long basicInfoId;
    private String empId;
    private String name;
    private Integer gender;
    private String company;

    @Override
    public String toString() {
        return "BasicInfo{" +
                "basicInfoId=" + basicInfoId +
                ", empId='" + empId + '\'' +
                ", name='" + name + '\'' +
                ", gender=" + gender +
                ", company='" + company + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof BasicInfo) {
            //根據真實數據情況而定
            return ((BasicInfo) o).basicInfoId == this.basicInfoId;
        }
        return false;
    }

    @Override
    public int hashCode() {
        //根據真實數據情況而定
        return this.basicInfoId.intValue();
    }
}
public static void main(String[] args) {
    //模擬數據
    BasicInfo info1 = new BasicInfo(111L, "Key001", "張三", 1, "阿狸");
    BasicInfo info2 = new BasicInfo(112L, "Key002", "李四", 0, "百毒");
    BasicInfo info3 = new BasicInfo(111L, "Key001", "張三", 1, "阿狸");
    BasicInfo info4 = new BasicInfo(111L, "Key001", "張三", 1, "阿狸");
    BasicInfo info5 = new BasicInfo(113L, "Key003", "王五", 1, "疼訊");
    BasicInfo info6 = new BasicInfo(112L, "Key002", "李四", 0, "百毒");
    List<BasicInfo> list = new ArrayList<BasicInfo>(Arrays.asList(info1,info2,info3,info4,info5,info6));
    //去重
    Set<BasicInfo> set = new HashSet<BasicInfo>(list);
    for (BasicInfo info : set) {
        System.out.println(info);
    }
}
//輸出:
//BasicInfo{basicInfoId=112, empId='Key002', name='李四', gender=0, company='百毒'}
//BasicInfo{basicInfoId=113, empId='Key003', name='王五', gender=1, company='疼訊'}
//BasicInfo{basicInfoId=111, empId='Key001', name='張三', gender=1, company='阿狸'}

注意:用 Lombok 的 @Data 註解會自動生成 get、set、equals、canEqual、hashCode、toString。

雖然不難,但是這是日常編碼中比較常用到的點,面試時也會偶爾被提起,還是很有必要去學習並應用的。


菜鳥本菜,不吝賜教,感激不盡!

更多題解源碼和學習筆記:githubCSDNM1ng

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章