HashCode和equal方法
equals()反映的是對象或變量具體的值,即兩個對象里面包含的值--可能是對象的引用,也可能是值類型的值。
而hashCode()是對象或變量通過哈希算法計算出的哈希值。
之所以有hashCode方法,是因為在批量的對象比較中,hashCode要比equals來得快,很多集合都用到了hashCode,比如HashTable。
?
兩個obj,如果equals()相等,hashCode()一定相等。
兩個obj,如果hashCode()相等,equals()不一定相等(Hash散列值有沖突的情況,雖然概率很低)。
所以:
可以考慮在集合中,判斷兩個對象是否相等的規(guī)則是:
第一步,如果hashCode()相等,則查看第二步,否則不相等;
第二步,查看equals()是否相等,如果相等,則兩obj相等,否則還是不相等。
?
1、首先equals()和hashcode()這兩個方法都是從object類中繼承過來的。
equals()是對兩個對象的地址值進(jìn)行的比較(即比較引用是否相同)。
hashCode()是一個本地方法,它的實現(xiàn)是根據(jù)本地機(jī)器相關(guān)的。
2、Java語言對equals()的要求如下,這些要求是必須遵循的:
A 對稱性:如果x.equals(y)返回是“true”,那么y.equals(x)也應(yīng)該返回是“true”。
B 反射性:x.equals(x)必須返回是“true”。
C 類推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也應(yīng)該返回是“true”。
D 一致性:如果x.equals(y)返回是“true”,只要x和y內(nèi)容一直不變,不管你重復(fù)x.equals(y)多少次,返回都是“true”。
任何情況下,x.equals(null),永遠(yuǎn)返回是“false”;x.equals(和x不同類型的對象)永遠(yuǎn)返回是“false”。
3、equals()相等的兩個對象,hashcode()一定相等;
反過來:hashcode()不等,一定能推出equals()也不等;
hashcode()相等,equals()可能相等,也可能不等。?
?
引用:http://blog.sina.com.cn/s/blog_59e0c16f0100xne7.html
1、為什么要重載equal方法?
答案:因為Object的equal方法默認(rèn)是兩個對象的引用的比較,意思就是指向同一內(nèi)存,地址則相等,否則不相等;如果你現(xiàn)在需要利用對象里面的值來判斷是否相等,則重載equal方法。
2、 為什么重載hashCode方法?
答案:一般的地方不需要重載hashCode,只有當(dāng)類需要放在HashTable、HashMap、HashSet等等hash結(jié)構(gòu)的集合時才會重載hashCode,那么為什么要重載hashCode呢?就HashMap來說,好比HashMap就是一個大內(nèi)存塊,里面有很多小內(nèi)存塊,小內(nèi)存塊里面是一系列的對象,可以利用hashCode來查找小內(nèi)存塊hashCode%size(小內(nèi)存塊數(shù)量),所以當(dāng)equal相等時,hashCode必須相等,而且如果是object對象,必須重載hashCode和equal方法。
3、 為什么equals()相等,hashCode就一定要相等,而hashCode相等,卻不要求equals相等?
答案:1、因為是按照hashCode來訪問小內(nèi)存塊,所以hashCode必須相等。
2、HashMap獲取一個對象是比較key的hashCode相等和equal為true。
之所以hashCode相等,卻可以equal不等,就比如ObjectA和ObjectB他們都有屬性name,那么hashCode都以name計算,所以hashCode一樣,但是兩個對象屬于不同類型,所以equal為false。
4、 為什么需要hashCode?
1、 通過hashCode可以很快的查到小內(nèi)存塊。
2、通過hashCode比較比equal方法快,當(dāng)get時先比較hashCode,如果hashCode不同,直接返回false。
?
hashCode()的作用
1.hashcode是用來查找的,如果你學(xué)過數(shù)據(jù)結(jié)構(gòu)就應(yīng)該知道,在查找和排序這一章有
例如內(nèi)存中有這樣的位置
0???? 1???? 2???? 3???? 4???? 5???? 6???? 7????
而我有個類,這個類有個字段叫ID,我要把這個類存放在以上8個位置之一,如果不用hashcode而任意存放,那么當(dāng)查找時就需要到這八個位置里挨個去找,或者用二分法一類的算法。
但如果用hashcode那就會使效率提高很多。
我們這個類中有個字段叫ID,那么我們就定義我們的hashcode為ID%8,然后把我們的類存放在取得得余數(shù)那個位置。比如我們的ID為9,9除8的余數(shù)為1,那么我們就把該類存在1這個位置,如果ID是13,求得的余數(shù)是5,那么我們就把該類放在5這個位置。這樣,以后在查找該類時就可以通過ID除8求余數(shù)直接找到存放的位置了。
2.但是如果兩個類有相同的hashcode怎么辦那(我們假設(shè)上面的類的ID不是唯一的),例如9除以8和17除以8的余數(shù)都是1,那么這是不是合法的,回答是:可以這樣。那么如何判斷呢?在這個時候就需要定義?? equals了。
也就是說,我們先通過?? hashcode來判斷兩個類是否存放某個桶里,但這個桶里可能有很多類,那么我們就需要再通過?? equals?? 來在這個桶里找到我們要的類。
那么。重寫了equals(),為什么還要重寫hashCode()呢?
想想,你要在一個桶里找東西,你必須先要找到這個桶啊,你不通過重寫hashcode()來找到桶,光重寫equals()有什么用啊
3。你要對A類排序,有兩種方法,一種就是讓A類實現(xiàn)comparabole結(jié)構(gòu)并實現(xiàn)compareTo()方法,那么可以通過Collections.sort(List??? list)對其進(jìn)行排序
另一種方法:自己定義一個類B實現(xiàn)Comparator類并實現(xiàn)compare方法,然后通過Collections.sort(List list,B b)進(jìn)行排序
hashCode()是用來產(chǎn)生哈?,?shù)?#xff0c;而哈?,斒怯脕碓谏⒘写鎯Y(jié)構(gòu)中確定對象的存儲地址的,(這一段在?? Java編程思想?? 中講的很清楚的)象util包中的帶hash的集合類都是用這種存儲結(jié)構(gòu):HashMap,HashSet,?他們在將對象存儲時(嚴(yán)格說是對象引用),需要確定他們的地址吧,而HashCode()就是這個用途的,一般都需要重新定義它的,因為默認(rèn)情況下,由?Object類定義的?hashCode?方法會針對不同的對象返回不同的整數(shù),這一般是通過將該對象的內(nèi)部地址轉(zhuǎn)換成一個整數(shù)來實現(xiàn)的,現(xiàn)在舉個例子來說,就拿HashSet來說?? ,在將對象存入其中時,通過被存入對象的hashCode()?來確定對象在HashSet中的存儲地址,通過equals()來確定存入的對象是否重復(fù),hashCode()?,equals()都需要自己重新定義,因為hashCode()默認(rèn)前面已經(jīng)說啦,而equals()?? 默認(rèn)是比較的對象引用,你現(xiàn)在想一下,如果你不定義equals()的話,那么同一個類產(chǎn)生的兩個內(nèi)容完全相同的對象都可以存入Set,因為他們是通過equals()來確定的,這樣就使得HashSet失去了他的意義,看一下下面這個:
import java.util.*;
public class Test {
??? public static void main(String[] args) {
??????? HashSet set = new HashSet();
??????? for (int i = 0; i <= 3; i++){
??????????? set.add(new Demo1(i,i));???????????
??????? }
??????? System.out.println(set);
??????? set.add(new Demo1(1,1));
??????? System.out.println(set);
??????? System.out.println(set.contains(new Demo1(0,0)));
??????? System.out.println(set.add(new Demo1(1,1)));
??????? System.out.println(set.add(new Demo1(4,4)));
??????? System.out.println(set);
??? }
??? private static class Demo1 {
??????? private int value;
???????
??????? private int id;
??????? public Demo1(int value,int id) {
??????????? this.value = value;
??????????? this.id=id;
??????? }
??????? public String toString() {
??????????? return " value = " + value;
??????? }
??????? public boolean equals(Object o) {
??????????? Demo1 a = (Demo1) o;
??????????? return (a.value == value) ? true : false;
??????? }
??????? public int hashCode() {
??????????? return id;
??????? }
??? }
}
你分別注釋掉hashCode()和?? equals()來比較一下他們作用就可以拉,關(guān)鍵要自己動手看看比較的結(jié)果你就可以記得很清楚啦
如果還不是很明確可以再看另一個例子:
import java.util.HashMap;
import java.util.Map;
public final class Test {
??? public static void main(String[] args) {
??????? Map m = new HashMap();
??????? m.put(new PhoneNumber(020, 12345678), "shellfeng");
??????? System.out.println(m.get(new PhoneNumber(020, 12345678)));
??? }
??? private static class PhoneNumber {
???????
??????? private short areaCode;
???????
??????? private short extension;
??????? public PhoneNumber(int areaCode, int extension) {
??????????? this.areaCode = (short) areaCode;
??????????? this.extension = (short) extension;
??????? }
??????? public boolean equals(Object o) {
??????????? if (o == this) {
??????????????? return true;
??????????? }
??????????? if (!(o instanceof PhoneNumber)) {
??????????????? return false;
??????????? }
??????????? PhoneNumber pn = (PhoneNumber) o;
??????????? return pn.extension == extension && pn.areaCode == areaCode;
??????? }
???????
??????? public int hashCode() {
??????????? int result = 17;
??????????? result = 37 * result + areaCode;
??????????? result = 37 * result + extension;
??????????? return result;
??????? }
??? }
}
還是那句話:你注釋掉hashCode()比較一下他們作用就可以拉,關(guān)鍵要自己動手看看比較的結(jié)果你就可以記得很清楚啦
總結(jié)
hashCode()方法使用來提高M(jìn)ap里面的搜索效率的,Map會根據(jù)不同的hashCode()來放在不同的桶里面,Map在搜索一個對象的時候先通過hashCode()找到相應(yīng)的桶,然后再根據(jù)equals()方法找到相應(yīng)的對象.要正確的實現(xiàn)Map里面查找元素必須滿足一下兩個條件:
(1)當(dāng)obj1.equals(obj2)為true時obj1.hashCode()?? ==?? obj2.hashCode()必須為true
(2)當(dāng)obj1.hashCode() == obj2.hashCode()為false時obj.equals(obj2)必須為false
Java中的集合(Collection)有兩類,一類是List,再有一類是Set。你知道它們的區(qū)別嗎?前者集合內(nèi)的元素是有序的,元素可以重復(fù);后者元素?zé)o序,但元素不可重復(fù)。
那么這里就有一個比較嚴(yán)重的問題了:要想保證元素不重復(fù),可兩個元素是否重復(fù)應(yīng)該依據(jù)什么來判斷呢?這就是Object.equals方法了。
但是,如果每增加一個元素就檢查一次,那么當(dāng)元素很多時,后添加到集合中的元素比較的次數(shù)就非常多了。
也就是說,如果集合中現(xiàn)在已經(jīng)有1000個元素,那么第1001個元素加入集合時,它就要調(diào)用1000次equals方法。這顯然會大大降低效率。
哈希算法也稱為散列算法,是將數(shù)據(jù)依特定算法直接指定到一個地址上。我們可以認(rèn)為hashCode方法返回的就是對象存儲的物理地址(實際可能并不是,例如:通過獲取對象的物理地址然后除以8再求余,余數(shù)幾是計算得到的散列值,我們就認(rèn)為返回一個不是物理地址的數(shù)值,而是一個可以映射到物理地址的值)。
這樣一來,當(dāng)集合要添加新的元素時,先調(diào)用這個元素的hashCode方法,就一下子能定位到它應(yīng)該放置的物理位置上。如果這個位置上沒有元素,它就可以直接存儲在這個位置上,不用再進(jìn)行任何比較了;如果這個位置上已經(jīng)有元素了,就調(diào)用它的equals方法與新元素進(jìn)行比較,相同的話就不存了,不相同就散列其它的地址。所以這里存在一個沖突解決的問題。這樣一來實際調(diào)用equals方法的次數(shù)就大大降低了,幾乎只需要一兩次。
總結(jié)
以上是生活随笔為你收集整理的HashCode和equal方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入浅出JVM
- 下一篇: why在重写equals时还必须重写ha