Java中對List對象集合使用HashSet自動去重無法生效問題的解決

一、需求描述

  1. 使用SQL語句關聯三個表後,在Debug測試的過程中,獲取數據庫中的記錄,發現有好幾條記錄是重複的。經過問題的排查,導致關聯的結果出現重複,是因爲其中一個表中含有多條重複的歷史記錄。
  2. 需要有一種辦法將返回的數據進行條件過濾,將重複的記錄只保持一條,所以想到了Set集合。

二、補充

  1. Set集合的特點(其中2點):
    (1)集合元素不重複
    (2)集合元素無序
  2. 底層原理簡述
    HashSet底層是HashMap實現,value固定爲一個自定義對象,使用key保證集合元素的唯一性(原理:確保元素唯一性的兩個方法,hashCode()和equals()方法。當調用add()方法向集合存入對象時,先比較此對象與原有對象的哈希值是否一樣,如果不一樣,直接存入集合,如果哈希值一樣,就會繼續比較這兩個對象是否爲同一個對象,就會調用equals()方法)總之,只有HashCode的相同時,纔會調用equals()方法。但它不保證集合元素的順序(HashSet底層HashMap一定無序)

三、去重過程

//主函數如下所示:
 public static void main(String[] args) {
        List<Staff> list = new ArrayList<>();
        list.add(new Staff(1,"李華","[email protected]",111,"13115448748","隔壁小王","人工智能系統"));
        list.add(new Staff(1,"李華","[email protected]",111,"13115448748","隔壁小王","人工智能系統"));
        Set<Staff> set = new HashSet<>(list);
        //set.addAll(list);
        List<Staff> list1 = new ArrayList<>(set);
        System.out.println(list1.size());
    }
//最初的員工實體類
package com.ml.entry;
public class Staff {
    private Integer managerId;
    private String manager;
    private String manager_email;
    private Integer staffId;
    private String staffNum;
    private String staffName;
    private String sysName;

    public Integer getManagerId() {
        return managerId;
    }

    public void setManagerId(Integer managerId) {
        this.managerId = managerId;
    }

    public String getManager() {
        return manager;
    }

    public void setManager(String manager) {
        this.manager = manager;
    }

    public String getManager_email() {
        return manager_email;
    }

    public void setManager_email(String manager_email) {
        this.manager_email = manager_email;
    }

    public Integer getStaffId() {
        return staffId;
    }

    public void setStaffId(Integer staffId) {
        this.staffId = staffId;
    }

    public String getStaffNum() {
        return staffNum;
    }

    public void setStaffNum(String staffNum) {
        this.staffNum = staffNum;
    }

    public String getStaffName() {
        return staffName;
    }

    public void setStaffName(String staffName) {
        this.staffName = staffName;
    }

    public String getSysName() {
        return sysName;
    }

    public void setSysName(String sysName) {
        this.sysName = sysName;
    }

    @Override
    public String toString() {
        return "Staff{" +
                "managerId=" + managerId +
                ", manager='" + manager + '\'' +
                ", manager_email='" + manager_email + '\'' +
                ", staffId=" + staffId +
                ", staffNum='" + staffNum + '\'' +
                ", staffName='" + staffName + '\'' +
                ", sysName='" + sysName + '\'' +
                '}';
    }
}

//修改之後的員工實體類
package com.ml.entry;
public class Staff {
    private Integer managerId;
    private String manager;
    private String manager_email;
    private Integer staffId;
    private String staffNum;
    private String staffName;
    private String sysName;

   @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Staff staff = (Staff) o;
        return Objects.equals(managerId, staff.managerId) &&
                Objects.equals(manager, staff.manager) &&
                Objects.equals(manager_email, staff.manager_email) &&
                Objects.equals(staffId, staff.staffId) &&
                Objects.equals(staffNum, staff.staffNum) &&
                Objects.equals(staffName, staff.staffName) &&
                Objects.equals(sysName, staff.sysName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(managerId, manager, manager_email, staffId, staffNum, staffName, sysName);
    }
    public Integer getManagerId() {
        return managerId;
    }

    public void setManagerId(Integer managerId) {
        this.managerId = managerId;
    }

    public String getManager() {
        return manager;
    }

    public void setManager(String manager) {
        this.manager = manager;
    }

    public String getManager_email() {
        return manager_email;
    }

    public void setManager_email(String manager_email) {
        this.manager_email = manager_email;
    }

    public Integer getStaffId() {
        return staffId;
    }

    public void setStaffId(Integer staffId) {
        this.staffId = staffId;
    }

    public String getStaffNum() {
        return staffNum;
    }

    public void setStaffNum(String staffNum) {
        this.staffNum = staffNum;
    }

    public String getStaffName() {
        return staffName;
    }

    public void setStaffName(String staffName) {
        this.staffName = staffName;
    }

    public String getSysName() {
        return sysName;
    }

    public void setSysName(String sysName) {
        this.sysName = sysName;
    }

    @Override
    public String toString() {
        return "Staff{" +
                "managerId=" + managerId +
                ", manager='" + manager + '\'' +
                ", manager_email='" + manager_email + '\'' +
                ", staffId=" + staffId +
                ", staffNum='" + staffNum + '\'' +
                ", staffName='" + staffName + '\'' +
                ", sysName='" + sysName + '\'' +
                '}';
    }
}

運行主函數測試結果顯示,通過Set集合自動去重之後,集合中還是含有兩個對象,並沒有去重。
通過仔細的分析,問題原來出現在員工實體類對象中。原來對於自定義的實體類,若想通過Set集合對List集合達到去重效果的話,需要在自定義的實體類中重寫hashCode()和equals()方法。

四、過程分析

  1. 首先我們看一下HashSet的具體實現,就拿add()方法來分析一下。
    在這裏插入圖片描述
    方框1處分析:

  2. HashSet集合對象是如何判斷數據元素是否重複的呢?
    (1)首先會檢查待存對象key的hashCode值是否與集合中已有元素對象hashCode值相同,如果hashCode不同則表示不重複, 如果hashCode相同那麼會再調用equals方法進一步檢查,equals返回true表示對象已存在,否則表示不存在。
    注意: 在Java中,hashCode值是由存儲對象的地址所確定的,每一個地址對應一個hashCode值。

  3. 所以回到我們問題初始的地方,似乎就能找到答案了,當我們在向Staff對象添加兩個相同對象值的時候,爲什麼經過HashSet處理之後,理應去重,但是實際輸出結果卻未去重。

  4. 原因就在於我們通過使用new Staff();對象創建實際是創建了兩個不同的對象,也就對應了兩個地址進行存儲對象,所以Ha shSet在if判斷的時候,對於HashCode不同,就返回了false,證明集合中是兩個不同的對象值,這樣就使得最後集合中並沒有去重。
    在這裏插入圖片描述
    在這裏插入圖片描述

  5. 所以通過這個小的案例,我們可以明白一個道理。當我們想要對集合對象達到去重效果的話,我們需要在自定義的Staff類中重寫hashCode方法,讓id值相同的Staff對象的hashCode值相同,這樣才能讓id相同的對象通過上面判斷條件的第一關。

  6. 兩個對象的Student對象就會在4.1方框1的if判斷的p.hash == hash返回true而進入下一個判斷條件,而由於(k = p.key) == key比較的是兩個對象的地址,所以自然是false,接着就進入key != null && key.equals(k)的判斷,而由於Staff類中沒有equals方法,所以這裏調用的是原生的Object類中equals方法,而通過查找我們發現,Object類中的equals方法比對的仍然是對象的地址,這樣返回的結果還是false,那麼這樣肯定是不行的,所以我們這裏就需要對Staff類做一些處理;

  7. 解決辦法就是在Staff類中重寫equals方法,來達到將這兩個Student對象判斷爲一個對象的目的,下圖就是我在Staff類中重寫的equals方法:
    在這裏插入圖片描述注意:在重寫equals方法的時候,需要考慮到調用這個方法的並不一定是Staff類所創建的對象,這時就需要添加一個if語句去判斷調用這個方法的對象是否爲Staff類的實例,如果是,那麼我們就將它的元素值與集合中已有對象的元素值做對比。

五、過程總結

  1. HashSet達到去重的二要素:
    (1)重寫HashCode方法
    (2)重寫equals方法
  2. 兩個方法重寫的目的:
    (1)重寫HashCode方法目的是保證兩個對象之間的HashCode相同
    (2)重寫equals方法目的是保證兩個對象爲一個相同對象
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章