HashSet總結及例項,重寫equals()和hashCode()
HashSet的原始碼作總結:
HashSet在存元素時,會呼叫物件的hashCode方法計算出儲存位置,然後和該位置上所有的元素進行equals比較。
給我們最直觀的感受就是兩點,不可重複和無序,底層採用了HashMap結構。
1. HashSet基於HashMap實現, 以HashSet的值作為HashMap的一個key, 以一個Object物件常量作為HashMap的值。
2. 根據HashMap的特性,可以推敲出:HashSet允許擁有1個為null的值, HashSet的值不可重複。
3. 在建立HashSet的時候,如果合適,最好指定其內部HashMap的 capacity和loadFactory的值, 至於原因,在介紹HashMap的時候,提到過。
-
HashSet的底層通過HashMap實現的。而HashMap在JDK1.7之前使用的是陣列+連結串列實現,在1.8+使用的陣列+連結串列+紅黑樹實現。其實也可以這樣理解,HashSet的底層實現和HashMap使用的是相同的方式,因為Map是無序的,因此HashSet也無法保證順序。
-
HashSet的方法,也是藉助HashMap的方法來實現的。
例項:
set需要重寫equals()方法和HashCode()方法。 若要遍歷輸出,還得重寫下toString()
Student類:(兩個屬性,cardId和name)
我們在Studnet類中重寫hashCode()和equals()。使用eclipse直接右鍵"Source->Generate hashCode() and equals()..."自動生成程式碼。
package Hash; public class Student { int cardId; String name; public int getCardId() { return cardId; } public void setCardId(int cardId) { this.cardId = cardId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Student(int cardId,String name) { this.cardId = cardId; this.name = name; } public String toString() { return "Student = [carId="+cardId+",name="+name+"]"; } @Override public int hashCode() { //重寫hashCode() final int prime = 31; int result = 1; result = prime * result + cardId; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { //重寫equals() if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student other = (Student) obj; if (cardId != other.cardId) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
主函式:
package Hash;
import java.util.HashSet;
public class HashSetDemo {
public static void main(String[] args) {
Student s1 = new Student(111,"zhangsan");
Student s2 = new Student(111,"zhangsan");
Student s3 = new Student(333,"lisi");
Student s4 = new Student(444,"wangwu");
HashSet<Student> s = new HashSet<>();
s.add(s1);
s.add(s2);
s.add(s3);
s.add(s4);
for(Student n : s) { //遍歷
System.out.println(n);
}
}
}
輸出結果
Student = [carId=333,name=lisi]
Student = [carId=111,name=zhangsan]
Student = [carId=444,name=wangwu]
總結
因為HashMap在put一個Key時會判斷,將要放進去的Key的hash值與 目前HashMap中定位到的那個Key的hash值比較。
如果hash值相當,繼續比較 這兩個物件的地址或者內容是否相當。
如果相當:判斷出來要新增的Key與HashMap中的Key重複,把Value的值給替換成最新的。
HashSet中的Value是一個固定值PRESENT。 所以修改不修改無所謂。
重寫equals方法後最好重寫hashCode方法,否則兩個等價物件可能得到不同的hashCode,這在集合框架中使用可能產生嚴重後果,這樣如果我們對一個物件重寫了euqals,意思是隻要物件的成員變數值都相等那麼euqals就等於true,但不重寫hashcode,那麼我們再new一個新的物件,
當原物件.equals(新物件)等於true時,兩者的hashcode卻是不一樣的,由此將產生了理解的不一致,如在儲存雜湊集合時(如Set類),將會儲存了兩個值一樣的物件,導致混淆,因此,就也需要重寫hashcode()。