1.hashcode是用來查找的,如果你學(xué)過數(shù)據(jù)結(jié)構(gòu)就應(yīng)該知道,在查找和排序這一章有
例如內(nèi)存中有這樣的位置
0 1 2 3 4 5 6 7
而我有個(gè)類,這個(gè)類有個(gè)字段叫ID,我要把這個(gè)類存放在以上8個(gè)位置之一,如果不用hashcode而任意存放,那么當(dāng)查找時(shí)就需要到這八個(gè)位置里挨個(gè)去找,或者用二分法一類的算法。
但如果用hashcode那就會(huì)使效率提高很多。
我們這個(gè)類中有個(gè)字段叫ID,那么我們就定義我們的hashcode為ID%8,然后把我們的類存放在取得得余數(shù)那個(gè)位置。比如我們的ID為9,9除8的余數(shù)為1,那么我們就把該類存在1這個(gè)位置,如果ID是13,求得的余數(shù)是5,那么我們就把該類放在5這個(gè)位置。這樣,以后在查找該類時(shí)就可以通過ID除8求余數(shù)直接找到存放的位置了。
2.但是如果兩個(gè)類有相同的hashcode怎么辦那(我們假設(shè)上面的類的ID不是唯一的),例如9除以8和17除以8的余數(shù)都是1,那么這是不是合法的,回答是:可以這樣。那么如何判斷呢?在這個(gè)時(shí)候就需要定義 equals了。
也就是說,我們先通過 hashcode來判斷兩個(gè)類是否存放某個(gè)桶里,但這個(gè)桶里可能有很多類,那么我們就需要再通過 equals 來在這個(gè)桶里找到我們要的類。
那么。重寫了equals(),為什么還要重寫hashCode()呢?
想想,你要在一個(gè)桶里找東西,你必須先要找到這個(gè)桶啊,你不通過重寫hashcode()來找到桶,光重寫equals()有什么用啊
3。你要對(duì)A類排序,有兩種方法,一種就是讓A類實(shí)現(xiàn)comparabole結(jié)構(gòu)并實(shí)現(xiàn)compareTo()方法,那么可以通過Collections.sort(List <A> list)對(duì)其進(jìn)行排序
另一種方法:自己定義一個(gè)類B實(shí)現(xiàn)Comparator類并實(shí)現(xiàn)compare方法,
然后通過Collections.sort(List <A> list,B b)進(jìn)行排序
hashCode()是用來產(chǎn)生哈希瑪?shù)模,斒怯脕碓谏⒘写鎯?chǔ)結(jié)構(gòu)中確定對(duì)象的存儲(chǔ)地址的,(這一段在 Java編程思想 中講的很清楚的)象util包中的 帶 hash 的集合類都是用這種存儲(chǔ)結(jié)構(gòu) :HashMap,HashSet, 他們?cè)趯?duì)象存儲(chǔ)時(shí)(嚴(yán)格說是對(duì)象引用),需要確定他們的地址吧, 而HashCode()就是這個(gè)用途的,一般都需要重新定義它的,因?yàn)槟J(rèn)情況下,由 Object 類定義的 hashCode 方法會(huì)針對(duì)不同的對(duì)象返回不同的整數(shù),這一般是通過將該對(duì)象的內(nèi)部地址轉(zhuǎn)換成一個(gè)整數(shù)來實(shí)現(xiàn)的,現(xiàn)在舉個(gè)例子來說, 就拿HashSet來說 ,在將對(duì)象存入其中時(shí),通過被存入對(duì)象的 hashCode() 來確定對(duì)象在 HashSet 中的存儲(chǔ)地址,通過equals()來確定存入的對(duì)象是否重復(fù),hashCode() ,equals()都需要自己重新定義,因?yàn)閔ashCode()默認(rèn)前面已經(jīng)說啦,而equals() 默認(rèn)是比較的對(duì)象引用,你現(xiàn)在想一下,如果你不定義equals()的話,那么同一個(gè)類產(chǎn)生的兩個(gè)內(nèi)容完全相同的對(duì)象都可以存入Set,因?yàn)樗麄兪峭ㄟ^equals()來確定的,這樣就使得HashSet 失去了他的意義,看一下下面這個(gè):
import java.util.*;
/**
*類說明
* @author shellfeng E-mail:lsf830804@yahoo.com.cn
* @version 1.0
*
*/
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)鍵要自己動(dòng)手看看比較的結(jié)果你就可以記得很清楚啦
如果還不是很明確可以再看另一個(gè)例子:
import java.util.HashMap;
import java.util.Map;
/**
*
* 類說明
*
* @author shellfeng E-mail:lsf830804@yahoo.com.cn
* @version 1.0
*/
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 {
/**
* 區(qū)號(hào)
*/
private short areaCode;
/**
* 擴(kuò)展號(hào)
*/
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;
}
/**
* @see java.lang.Object#hashCode()
* @return result就是我們得到的散列值,其實(shí)我們的計(jì)算過程可以多種,這里只不過是一個(gè)例子,需要你的靈活運(yùn)用,使其接近你需要的理想結(jié)果
*/
public int hashCode() {
int result = 17;
result = 37 * result + areaCode;
result = 37 * result + extension;
return result;
}
}
}
還是那句話:你注釋掉hashCode()比較一下他們作用就可以拉,關(guān)鍵要自己動(dòng)手看看比較的結(jié)果你就可以記得很清楚啦
總結(jié)
hashCode()方法使用來提高M(jìn)ap里面的搜索效率的,Map會(huì)根據(jù)不同的hashCode()來放在不同的桶里面,Map在搜索一個(gè)對(duì)象的時(shí)候先通過hashCode()找到相應(yīng)的桶,然后再根據(jù)equals()方法找到相應(yīng)的對(duì)象.要正確的實(shí)現(xiàn)Map里面查找元素必須滿足一下兩個(gè)條件:
(1)當(dāng)obj1.equals(obj2)為true時(shí)obj1.hashCode() == obj2.hashCode()必須為true
(2)當(dāng)obj1.hashCode() == obj2.hashCode()為false時(shí)obj.equals(obj2)必須為false
Java中的集合(Collection)有兩類,一類是List,再有一類是Set。你知道它們的區(qū)別嗎?前者集合內(nèi)的元素是有序的,元素可以重復(fù);后者元素?zé)o序,但元素不可重復(fù)。
那么這里就有一個(gè)比較嚴(yán)重的問題了:要想保證元素不重復(fù),可兩個(gè)元素是否重復(fù)應(yīng)該依據(jù)什么來判斷呢?這就是Object.equals方法了。
但是,如果每增加一個(gè)元素就檢查一次,那么當(dāng)元素很多時(shí),后添加到集合中的元素比較的次數(shù)就非常多了。
也就是說,如果集合中現(xiàn)在已經(jīng)有1000個(gè)元素,那么第1001個(gè)元素加入集合時(shí),它就要調(diào)用1000次equals方法。這顯然會(huì)大大降低效率。
哈希算法也稱為散列算法,是將數(shù)據(jù)依特定算法直接指定到一個(gè)地址上。我們可以認(rèn)為hashCode方法返回的就是對(duì)象存儲(chǔ)的物理地址(實(shí)際可能并不是,例如:通過獲取對(duì)象的物理地址然后除以8再求余,余數(shù)幾是計(jì)算得到的散列值,我們就認(rèn)為返回一個(gè)不是物理地址的數(shù)值,而是一個(gè)可以映射到物理地址的值)。
這樣一來,當(dāng)集合要添加新的元素時(shí),先調(diào)用這個(gè)元素的hashCode方法,就一下子能定位到它應(yīng)該放置的物理位置上。如果這個(gè)位置上沒有元素,它就可以直接存儲(chǔ)在這個(gè)位置上,不用再進(jìn)行任何比較了;如果這個(gè)位置上已經(jīng)有元素了,就調(diào)用它的equals方法與新元素進(jìn)行比較,相同的話就不存了,不相同就散列其它的地址。所以這里存在一個(gè)沖突解決的問題。這樣一來實(shí)際調(diào)用equals方法的次數(shù)就大大降低了,幾乎只需要一兩次。
上面只是我個(gè)人的一下理解,有不當(dāng)之處請(qǐng)大家指教