1. 程式人生 > >HashSet總結及例項,重寫equals()和hashCode()

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()。