摘要:不同的對象,建議有不同的。所以最好是讓均勻的落在不同的。正確寫法如下要對兩個字段做聯合去重時皮一下做,做,放入結果可能為這種情況就是違反了第條,放入了不同的。
我的第一篇文章
以前都在公司的文檔里寫,后來想想,還是自己找個地方記錄下來吧。
今天有個朋友問我hashcode的問題,記錄下來,并稍微讀下書尋求一點理論知識。
問題如下
有一個屬性都是字符串的對象,想放入hashset中,要求,對某一個屬性,相同就能放入,不同就不能放入。
朋友的問題是,知道equals咋寫,但是不知道hashcode咋寫,沒有思路。
我的理解是:hashcode是一種比equals粗粒度的比較,打個比方,兩個三位數,可以拿十位+個位數作為hashcode,百位+十位+個位才是真正的equals。也就是說,先比較十位+個位數hashcode,如果hashcode不一致,那么這兩個對象必然不一致,就不用繼續對比了,如果十位+個位數一致,那么他們有可能是一致的,這時繼續對比equals,才能知道是否兩個對象真的一致。當然這只是一種粗淺的理解,真正的理解還得看((h = key.hashCode()) ^ (h >>> 16)) & (n-1),直觀上可以理解為就是hash值的補碼取后幾位,問題也不大。
public class InnerClass { String a; String b; String c; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } InnerClass that = (InnerClass)o; return this.a.equals(that.a); }
如果只是尋求一個解,而不是尋求較優的解的話,那么很簡單。不是要比equals粗粒度的比較嗎?隨便來一個比equals粗一丁點的就可以了。如下
@Override public int hashCode() { return Objects.hash(a); }
這個寫法得到的結果是對的,但是我總覺得不好,因為在我的理解里,hashcode也是一種效率上的考慮。這樣的話,和直接比較this.a又有多大區別呢?
如果強行要粗粒度,我想了一種方法,例如:
@Override public int hashCode() { return Objects.hash(a)/3; }
這樣其實也增加不了效率,因為真正要比較的可以理解為hashcode的后幾位,除不除以3,影響不大。
假設問題變成:對a和b相同的不放入set,那么hashcode用Object.hash(a)才是一種相對不錯的方式。既滿足了要求,又有一定的效率上的提升。
這么閉門造車不是個辦法,拜讀一下《effective java》2e.
整理如下:
1.對同一個對象調用hashcode時,必須返回同樣的值。
2.equals相同的對象,必須有相同的hashcode。
3.equals不同的對象,建議有不同的hashcode。
4.當不重寫hashcode時,map.put(new A("a"), "b")之后,map.get(new A("a"))不一定能取到"b",因為沒重寫hashcode,put和get時的兩個對象,都是用的Object的hashcode,因為兩次new是兩個不同的對象,所以hashcode不相同,落在不同的bucket。(即便恰好落在相同的bucket,也不一定能獲取到值)
5.hashcode不要返回一個固定值。返回固定值會導致,所有值都落在同一個bucket,這樣程序的時間復雜度會增加。所以最好是讓hashcode均勻的落在不同的bucket。
6.hashcode均勻落在bucket的一個良好實踐是:
1.一個初始值 2.對對象的每一個字段,計算一個值(boolean:f?1:0/byte、char、short、int (int)f/long int(f^(f>>>32))/double Double.doubleToLongBits(f),然后按long處理/對象,對對象的每個字段調用hashcode 3.result = 31*result + c;
7.對不包含在equals里對比的字段,在hashcode中排除掉。
8.對有意義的字段(即equals里對比的字段),不要在hashcode中排除。例如要對a、b做equals,只對a做hashcode,那么可能hashcode在bucket的分布就不均勻了,性能就會下降。
根據這些規則,發現以前的很多想法都是錯誤的。正確寫法如下:
@Override public int hashCode() { int result = 17; result = 31 * result + a.hashCode(); return result; }
要對a、b兩個字段做聯合去重時:
@Override public int hashCode() { int result = 17; result = 31 * result + a.hashCode(); result = 31 * result + b.hashCode(); return result; }
皮一下:
a做equals,a、b做hashcode,放入hashset結果可能為
b a e
a b c
b c e
這種情況就是違反了第2條,放入了不同的bucket。
之前那個優化的想法不對(ab做equals,a做hash),因為b可能會導致hashcode在bucket的分布不均勻。
補充:bucket是什么? bucket就是hashmap中包含一系列entry的東西,每個bucket是一個鏈表或者一顆樹。
TODO: a.hashcode() object.hash(a) object.hashcode(a) 的區別
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73763.html
摘要:如果我們不重寫的方法,那么就會默認調用的方法小王小王我們可以看到以上的運行結果違背了的規定如果返回,那么方法必須返回相同的整數所以我們需要對對象的方法進行重寫通過重寫讓其與對象的屬性關聯起來,那么就能夠達到為,那么的值也相等。 面試官讓你說說==和equals()的區別,重寫equals必須重寫hashcode方法嗎 本身特質來說 ==:操作符 equals():方法 適用...
閱讀原文:不同時重寫equals和hashCode又怎樣! 可能一問到equals和hashCode相關的問題,就會有人講他們的自反性,對稱性,一致性,傳遞性等幾條約定了,此時我不得不佩服,這么多約定竟然都能記得,但我不知道你是不是真的理解呢。 我不同時重寫又能如何呢? showImg(https://segmentfault.com/img/remote/1460000018795219); 我...
摘要:介紹的作用是獲取哈希碼,也稱為散列碼它實際上是返回一個整數。所以具有相索引的對象,在該散列碼位置處存在多個對象,我們必須依靠的和本身來進行區分。 1.hashCode介紹 hashCode() 的作用是獲取哈希碼,也稱為散列碼;它實際上是返回一個int整數。這個散列碼的作用是確定該對象在散列表中的索引位置,如果有看我的上一篇文章 什么是散列表,那么這里的散列碼就相當于上文中根據首字母查...
摘要:它也是用來判斷兩個對象是否相等,所以也得分不同的情況來說明。什么是的作用是獲取哈希碼,也稱為散列碼它返回的一個整數。這個哈希碼的作用是確定該對象在哈希表中的索引位置。它定義在的中,這就意味著中的任何類都包含有函數。 前言 萬丈高樓平地起,今天的聊點基礎而又經常讓人忽視的話題,比如==與equals()區別?為何當我們重寫完equals()后也要有必要去重寫hashcode()呢? .....
摘要:所以在對象沒有重寫這個方法時,默認使用此方法,即比較對象的內存地址值。結果為可以發現不管對象的內存地址是否相同并不影響其結果,所以類型比較的是數據值而不是內存地址值。 showImg(https://segmentfault.com/img/bVbqpku?w=800&h=344); 今天朋友突然問到一個問題: 兩個對象使用x.equals(y)判斷結果為true時,兩個對象的hash...
閱讀 841·2021-11-15 17:58
閱讀 3641·2021-11-12 10:36
閱讀 3779·2021-09-22 16:06
閱讀 956·2021-09-10 10:50
閱讀 1325·2019-08-30 11:19
閱讀 3309·2019-08-29 16:26
閱讀 928·2019-08-29 10:55
閱讀 3341·2019-08-26 13:48