HashSet要重寫equals方法和hashCode方法
注:重要筆記在程式碼中註釋有
hashSet去重:
即判斷兩個物件是否相等
1:會先呼叫物件的hashCode方法獲得hash的值,如果set中雜湊表裡面沒有對應的hash值,則將次物件存入set中
2:如果set中hash表裡面有對應的hash值,就讓後面的物件呼叫equals方法和之前的hash值不同的物件進行比較,如果返回為true就證明存在,不在儲存,入夥返回為false則視為新物件,加入到set中。
對於hashset不可重複,如何判斷對新加入的物件是否重複
public int hashCode() {
// TODO Auto-generated method stub
// 1:int 整數
// 2:為了區分不相同 一般使用素數 不易產生碰撞
// 3:與屬性值有一定的關聯
int prim = 31;
int resultCode = prim*(prim+1) + (brand==null?0:brand.hashCode());
return resultCode;
}
// equals方法是object的方法
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
// 記憶體地址 引用傳遞是否相等
if (this == obj) {
return true;
}
// 空判斷
if (obj == null) {
return false;
}
// 判斷是否同屬於一個類
if (getClass() != obj.getClass()) {
return false;
}
Phone myPhone =(Phone)obj;
// 判斷屬性 brand price
return this.brand.equals(myPhone.brand)&&this.price==myPhone.price;
}
Java程式設計使用HashSet新增物件時,由於要符合Set的特點(沒順序,不重複)所以必須重寫equals方法和hashCode方法。為什麼要這樣呢?請看:Java中關於HashSet新增自定義物件時,自定義類要重寫equals方法和hashCode方法的前世今生...
第一:Set集合沒有順序,也不允許重複。為什麼要這樣:模擬現實的集合。這裡的重複只是:物件的重複何為物件的重複:指的就是同一個物件。何為同一個物件:記憶體中,所在的記憶體編號一致。記憶體編號的表示是什麼:雜湊碼(雜湊碼一般是 類名 和 物件所在記憶體地址的十六進位制數字表示 的組合)。
第二:這種設定和實現中的矛盾在什麼地方:現實生活中只要屬性相同,我們就認為那是同一個物件。這與計算機比較同一個物件的方法不同(計算機使用記憶體地址,即雜湊碼)於是,就需要重寫equals方法和hashCode方法(&&)來讓程式的執行結果符合現實生活基本資料型別的實現類都已經重寫了上面的兩個方法。
第三:為什麼要重寫equals方法和hashCode方法(技術實現原理):程式向HashSet中新增一個物件時,先用hashCode方法計算出該物件的雜湊碼。比較: (1),如果該物件雜湊碼與集合已存在物件的雜湊碼不一致,則該物件沒有與其他物件重複,新增到集合中!(2),如果存在於該物件相同的雜湊碼,那麼通過equals方法判斷兩個雜湊碼相同的物件是否為同一物件(判斷的標準是:屬性是否相同) 1>,相同物件,不新增。 2>,不同物件,新增!
這時有兩個疑問:1,為什麼雜湊碼相同了還有可能是不同物件?2,為什麼經過比較雜湊碼還需要藉助equals方法判斷?
答:首先:按照Object類的hashCode方法,是不可能返回兩個相同的雜湊碼的。(雜湊碼唯一標誌了物件) 然後:Object類的hashCode方法返回的雜湊碼具有唯一性(地址唯一性),但是這樣不能讓程式的執行邏輯符合現實生活。(這個邏輯就是:屬性相同的物件被看作同一個物件。)為了讓程式的執行邏輯符合現實生活,Object的子類重寫了hashCode的方法(基本資料型別的實現類都已經重寫了兩個方法,自定義的類要軟體工程 師自己重寫。)
那麼:重寫的宗旨是什麼?重寫就是為了實現這樣的目的:屬性相同的不同物件在呼叫其hashCode方法後,返回的是同樣的雜湊碼。但是我們在重寫的時候,發現幾乎所有的寫法都無法避免一個bug:有一些屬性不同的物件(當然是不同的物件),會返回相同的雜湊碼。(即 重碼)
最後:為了解決這個問題:在雜湊碼相同的時候,再用equals方法比較兩個物件的對應屬性是否相同,這樣,確保了萬無一失。這樣:上面兩個問題得到解決。
/**
*
*/
package com.zhiyou.R;
import java.util.HashSet;
import java.util.Set;
/**
* @author Administrator
*
*/
public class HashTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// set中去重,依據什麼?
// 去重依據的並不是記憶體地址,依據的是雜湊值。
// 雜湊值就是一個int型別的整數
String string1 = new String("laosun");
String string2 = new String("laosun");
System.out.println(string1.hashCode());
System.out.println(string2.hashCode());
String string3 = new String("Aa");
String string4 = new String("BB");
System.out.println(string3.hashCode());
System.out.println(string4.hashCode());
// 雜湊值任何物件都存在的,因為hashCode()是object的方法
// hash值不相同 ,兩個元資料一定不相等
// hash值相同,兩個元資料不一定相等
// hash值猶如一個人的人名
// 兩個人名字不相同,肯定是不同的兩個人
// 名字相同,不一定指的是同一個人,可能重名
// set中去重,指的是先判斷已經存在的物件的hash值,如果新加入的物件的
// hash值和之前的的不一樣,那麼加入的就是一個新的物件,但是如果相等
// ,還仍需要判斷兩個物件是否相等(重寫equals方法)判斷物件中屬性值
// 是否和之前的物件的屬性相同,相同代表一樣,不同代表新的物件
// 既然hashCode()是第一優先要獲得的,但是hash值一樣,也有可能原內容不相同
// 所以對於自定義類Phone,必須同時實現hashCode()和equals(0這兩個方法。
// hashCode存在的意義是可以快速的判斷出來與之前大量的物件不一樣,
// 只要hash值不同代表就是一個新物件
aboutEqualsHashSet();
}
public static void aboutEqualsHashSet() {
Set<Phone> phone = new HashSet<>();
phone.add(new Phone("xiaomi",799));
phone.add(new Phone("xiaomi",799));
phone.add(new Phone("xiaomi",799));
phone.add(new Phone("xiaomi",799));
System.out.println(phone.size());
}
}
/**
*
*/
package com.zhiyou.R;
/**
* @author Administrator
*
*/
public class Phone {
private String brand;
private double price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Phone(String brand, double price) {
super();
this.brand = brand;
this.price = price;
}
public Phone() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Phone [brand=" + brand + ", price=" + price + "]";
}
// 將來Phone物件呼叫下面的方法活兒對應的hash值
// 依據brand的hash值來確定物件的hash值
@Override
public int hashCode() {
// TODO Auto-generated method stub
// 1:int 整數
// 2:為了區分不相同 一般使用素數 不易產生碰撞
// 3:與屬性值有一定的關聯
int prim = 31;
int resultCode = prim * (prim + 1) + (brand == null ? 0 : brand.hashCode());
return resultCode;
}
// equals方法是object的方法
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
// 記憶體地址 引用傳遞是否相等
if (this == obj) {
return true;
}
// 空判斷
if (obj == null) {
return false;
}
// 判斷是否同屬於一個類
if (getClass() != obj.getClass()) {
return false;
}
Phone myPhone = (Phone) obj;
// 判斷屬性 brand price 是否和myPhone的相等
// price
if (this.price != myPhone.price) {
return false;
}
// brand
if (this.brand == null) {
if (myPhone.brand == null) {
return false;
}
} else if (!this.brand.equals(myPhone.brand)) {
return false;
}
return true;
}
}