前言在程序設(shè)計(jì)中,有很多的“公約”,遵守約定去實(shí)現(xiàn)你的代碼,會(huì)讓你避開很多坑,這些公約是前人總結(jié)出來的設(shè)計(jì)規(guī)范。 Object類是Java中的萬類之祖,其中,equals和hashCode是2個(gè)非常重要的方法。 這2個(gè)方法總是被人放在一起討論。最近在看集合框架,為了打基礎(chǔ),就決定把一些細(xì)枝末節(jié)清理掉。一次性搞清楚! 下面開始剖析。
public boolean equals(Object obj)
Object類中默認(rèn)的實(shí)現(xiàn)方式是 : return this == obj 。那就是說,只有this 和 obj引用同一個(gè)對(duì)象,才會(huì)返回true。 而我們往往需要用equals來判斷 2個(gè)對(duì)象是否等價(jià),而非驗(yàn)證他們的唯一性。這樣我們?cè)趯?shí)現(xiàn)自己的類時(shí),就要重寫equals.
按照約定,equals要滿足以下規(guī)則。
自反性: x.equals(x) 一定是true 對(duì)null: x.equals(null) 一定是false 對(duì)稱性: x.equals(y) 和 y.equals(x)結(jié)果一致 傳遞性: a 和 b equals , b 和 c equals,那么 a 和 c也一定equals。 一致性: 在某個(gè)運(yùn)行時(shí)期間,2個(gè)對(duì)象的狀態(tài)的改變不會(huì)不影響equals的決策結(jié)果,那么,在這個(gè)運(yùn)行時(shí)期間,無論調(diào)用多少次equals,都返回相同的結(jié)果。
一個(gè)例子 1 class Test 2 { 3 private int num; 4 private String data; 5 6 public boolean equals(Object obj) 7 { 8 if (this == obj) 9 return true; 10 11 if ((obj == null) || (obj.getClass() != this.getClass())) 12 return false; 13 //能執(zhí)行到這里,說明obj和this同類且非null。 14 Test test = (Test) obj; 15 return num == test.num&& (data == test.data || (data != null && data.equals(test.data))); 16 } 17 18 public int hashCode() 19 { 20 //重寫equals,也必須重寫hashCode。具體后面介紹。 24 } 25 26 }
equals編寫指導(dǎo)Test類對(duì)象有2個(gè)字段,num和data,這2個(gè)字段代表了對(duì)象的狀態(tài),他們也用在equals方法中作為評(píng)判的依據(jù)。 在第8行,傳入的比較對(duì)象的引用和this做比較,這樣做是為了 save time ,節(jié)約執(zhí)行時(shí)間,如果this 和 obj是 對(duì)同一個(gè)堆對(duì)象的引用,那么,他們一定是qeuals 的。
if((obj == null) || (obj.getClass() != this.getClass()))
它違反了公約中的對(duì)稱原則。
dog instanceof Animal 得到true animal instanceof Dog 得到false
這就會(huì)導(dǎo)致 animal.equls(dog) 返回true
3、在具體比較對(duì)象的字段的時(shí)候,對(duì)于基本值類型的字段,直接用 == 來比較(注意浮點(diǎn)數(shù)的比較,這是一個(gè)坑)對(duì)于引用類型的字段,你可以調(diào)用他們的equals,當(dāng)然,你也需要處理字段為null 的情況。對(duì)于浮點(diǎn)數(shù)的比較,我在看Arrays.binarySearch的源代碼時(shí),發(fā)現(xiàn)了如下對(duì)于浮點(diǎn)數(shù)的比較的技巧: if ( Double.doubleToLongBits(d1) == Double.doubleToLongBits(d2) ) //d1 和 d2 是double類型 if( Float.floatToIntBits(f1) == Float.floatToIntBits(f2) ) //f1 和 f2 是d2是float類型
4、并不總是要將對(duì)象的所有字段來作為equals 的評(píng)判依據(jù),那取決于你的業(yè)務(wù)要求。比如你要做一個(gè)家電功率統(tǒng)計(jì)系統(tǒng),如果2個(gè)家電的功率一樣,那就有足夠的依據(jù)認(rèn)為這2個(gè)家電對(duì)象等價(jià)了,至少在你這個(gè)業(yè)務(wù)邏輯背景下是等價(jià)的,并不關(guān)心他們的價(jià)錢啊,品牌啊,大小等其他參數(shù)。
public int hashCode() 重寫了euqls方法的對(duì)象必須同時(shí)重寫hashCode()方法。 如果2個(gè)對(duì)象通過equals調(diào)用后返回是true,那么這個(gè)2個(gè)對(duì)象的hashCode方法也必須返回同樣的int型散列碼
在上面的例子中,Test類對(duì)象有2個(gè)字段,num和data,這2個(gè)字段代表了對(duì)象的狀態(tài),他們也用在equals方法中作為評(píng)判的依據(jù)。那么, 在hashCode方法中,這2個(gè)字段也要參與hash值的運(yùn)算,作為hash運(yùn)算的中間參數(shù)。這點(diǎn)很關(guān)鍵,這是為了遵守:2個(gè)對(duì)象equals,那么 hashCode一定相同規(guī)則。 也是說,參與equals函數(shù)的字段,也必須都參與hashCode 的計(jì)算。
hashCode編寫指導(dǎo)
在編寫hashCode時(shí),你需要考慮的是,最終的hash是個(gè)int值,而不能溢出。不同的對(duì)象的hash碼應(yīng)該盡量不同,避免hash沖突。 那么如果做到呢?下面是解決方案。
1、定義一個(gè)int類型的變量 hash,初始化為 7。 接下來讓你認(rèn)為重要的字段(equals中衡量相等的字段)參入散列運(yùn),算每一個(gè)重要字段都會(huì)產(chǎn)生一個(gè)hash分量,為最終的hash值做出貢獻(xiàn)(影響)
最后把所有的分量都總和起來,注意并不是簡(jiǎn)單的相加。選擇一個(gè)倍乘的數(shù)字31,參與計(jì)算。然后不斷地遞歸計(jì)算,直到所有的字段都參與了。 int hash = 7; hash = 31 * hash + 字段1貢獻(xiàn)分量; hash = 31 * hash + 字段2貢獻(xiàn)分量; ..... return hash;
說明,以下的內(nèi)容是我在google上找到并翻譯整理的,其中加入了自己的話和一些例子,便于理解,但我能保證這并不影響整體準(zhǔn)確性。 英文原文:http://www./journal/2002/10/equalhash.html
|
|