List去重為什么要寫equals(),hashCode()方法


一,各個集合的特點:

  •   Collection(集合):容器,用於存放對象(引用類型。基本類型需要自動裝箱)   
  •   List(列表):元素有序,元素可以重復 (有索引)。 通過元素的equals()方法判斷是否重復。  
  •   Set(集):元素無序,不可重復 (沒有索引)。 遍歷只能用Iterator迭代器和增強for, 不能使用普通for遍歷。 
  •   ArrayList(數組列表): 查詢快,增刪慢。   
  •   LinkedList(鏈表): 查詢慢,增刪快。
  •   HashSet(哈希表): 查詢快,增刪慢。 (底層其實就是Map) 。 存放的引用類型需重寫hashCode()和equals()方法。 
  •   LinkedHashSet(哈希鏈表): 查詢慢,增刪快。 有序的,存放順序和取出順序一致。

1.1,為什么要去重:

  •   在我們開發中,我們所讀取的數據難免會有重復數據,我們需要的則是不重復數據的引用,所以需要對數據進行去重,
  •   而基本數據類型的去重比較好去重而引用數據類型呢,因為要判斷hashCode運算是否相等,還有equals()是否相等,所以需要去重操作,
  •   我們以一個list集合為例,在該例中,我們將User實體類中姓名和電話號碼作為判斷該對象重復的標識,在User的實體類中我們重寫
  •   這兩個方法如下:
 1 package com.example.pojo;
 2 
 3 public class User {
 4     private String name;
 5     private String region;
 6     private Integer performance;
 7     public User(String name, String region, Integer performance) {
 8         super();
 9         this.name = name;
10         this.region = region;
11         this.performance = performance;
12     }
13     @Override
14     public String toString() {
15         return "User [name=" + name + ", region=" + region + ", performance=" + performance + "]";
16     }
17     @Override
18     public int hashCode() {
19         final int prime = 31;
20         int result = 1;
21         result = prime * result + ((name == null) ? 0 : name.hashCode());
22         result = prime * result + ((performance == null) ? 0 : performance.hashCode());
23         result = prime * result + ((region == null) ? 0 : region.hashCode());
24         return result;
25     }
26     @Override
27     public boolean equals(Object obj) {
28         if (this == obj)
29             return true;
30         if (obj == null)
31             return false;
32         if (getClass() != obj.getClass())
33             return false;
34         User other = (User) obj;
35         if (name == null) {
36             if (other.name != null)
37                 return false;
38         } else if (!name.equals(other.name))
39             return false;
40         if (performance == null) {
41             if (other.performance != null)
42                 return false;
43         } else if (!performance.equals(other.performance))
44             return false;
45         if (region == null) {
46             if (other.region != null)
47                 return false;
48         } else if (!region.equals(other.region))
49             return false;
50         return true;
51     }
52 }
  • 以上實體類中,我們在equals()方法中取出該對象的name與region和performance這三個屬性值去判斷比較,然后在重寫的hashCode()
  • 方法中返回這三個屬性值得equals對象地址值。

1.2,去重操作步驟:

 1 package com.example.arraylist;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Iterator;
 5 import java.util.List;
 6 import com.example.pojo.User;
 7 
 8 /**
 9  * @author Administrator
10  *    思路:
11  *        1.先對其文本文本進行添加,添加到List<User>中
12  *        2.把List<User>用迭代器進行迭代
13  *        3.創建一個新的集合,用來存儲不重復的元素
14  *        4.用while(it.hasNext())有多少元素就循環多少次    
15  *        4.判斷新的集合是否有舊的元素如果沒有則進行添加
16  */
17 public class ListUserRepeat {
18     public static void main(String[] args) {
19         String string="張三,河北,90\n"+
20                 "張三,河南,92\n"+
21                 "李四,湖北,80\n"+
22                 "王五,山西,88\n"+
23                 "張三,河北,90\n"+
24                 "李四,湖北,80\n"+
25                 "馬六,山東,77\n";
26         List<User> list = new ArrayList<>();
27         String[] split = string.split("\n");
28         for(String spl : split) {
29             String[] split2 = spl.split(",");
30             list.add(new User(split2[0], split2[1],Integer.parseInt(split2[2])));
31         }
32         Iterator<User> it = list.iterator();
33         List<User> listOne = new ArrayList<>();
34         while(it.hasNext()) {
35             Object object = it.next();
36             if(!listOne.contains(object)) {
37                 listOne.add((User) object);
38             }
39         }
40         for (User user : listOne) {
41             System.out.println(user);
42         }
43     }
44 }

運行這段代碼之后,就會很明顯的發現,list中重復的用戶名,地區,都相同的對象就被會認為是重復的元素而刪除掉,很明顯運行結果已經達到我們的目的。

二,說一下為什么需要重寫equals()方法和hashChode方法():

  • 一般情況下我們重寫equals()方法的時候還要重寫hashChode()方法,但是我們用的是list所以不用重寫hashCode,大家不妨
  • 可以試試上面的例子,在實體類將重寫的equals()方法注釋掉,再運行程序,這時就會發現運行結果並不是我們剛剛得到的結果,(圖中 我用的是list集合,
  • 不是set集合,list集合只需要重寫equals()方法就行,而set集合則equals()和hashCode()方法都需要重寫

2.1,String類中的equals()方法的源碼如下:

 1     public boolean equals(Object anObject) {
 2         if (this == anObject) {
 3             return true;
 4         }
 5         if (anObject instanceof String) {
 6             String anotherString = (String)anObject;
 7             int n = value.length;
 8             if (n == anotherString.value.length) {
 9                 char v1[] = value;
10                 char v2[] = anotherString.value;
11                 int i = 0;
12                 while (n-- != 0) {
13                     if (v1[i] != v2[i])
14                         return false;
15                     i++;
16                 }
17                 return true;
18             }
19         }
20         return false;
21     }

通過觀察equals()方法的源碼我們可以看出,該方法去比較兩個對象時,首先先去判斷兩個對象是否具有相同的地址,如果是同一個對象的引用,則直接放回true;如果地址不一樣,則證明不是引用同一個對象,接下來就是挨個去比較兩個字符串對象的內容是否一致,完全相等返回true,否則false。

2.2,String類中hashCode()方法的源碼如下:

 1     public int hashCode() {
 2         int h = hash;
 3         if (h == 0 && value.length > 0) {
 4             char val[] = value;
 5 
 6             for (int i = 0; i < value.length; i++) {
 7                 h = 31 * h + val[i];
 8             }
 9             hash = h;
10         }
11         return h;
12     }

以上是String類中重寫的hashCode()方法,在Object類中的hashCode()方法是返回對象的32位JVM內存地址,也就是說如果我們不去重寫該方法,將會返回該對象的32位JVM內存地址,以上我們測試的例子中,當注釋掉重寫的hashCode()方法時,這時默認返回對象的32JVM中的地址,兩個不同的對象地址顯然是不同的,我們在比較時,雖然通過重寫的equals()方法比較出來name和phoneNumber值是相同的,但是默認的hashCode()方法返回的值他們並不是同一個對象,所以我們通常要將hashCode()方法與equals()方法一起重寫,以維護hashCode方法的常規協定,該協定聲明相等對象必須具有相等的哈希碼。

總結:

  用白話說,通過hashCode判斷對象是否放在同一個桶里,先確定他們在一個桶里面,然后再通過equals方法去判斷這個桶里的對象是不是相同的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM