重寫 hashcode真有那么簡單嘛?( 三 )


public class Person {//用戶Id , 唯一確定用戶private String id;private String name;public Person(String id, String name) {this.id = id;this.name = name;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (!(o instanceof Person)) return false;Person person = (Person) o;return Objects.equals(id, person.id) && Objects.equals(name, person.name);}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}}@Testpublic void test() {HashMap<Person, Integer> map = new HashMap<>();//key:Person類型value:Integer類型Person personKey = new Person("1","張三");Person getKey = new Person("1","張三");map.put(personKey,100);System.out.println("原對象map.get():"+map.get(personKey));System.out.println("值相同的對象map.get():"+map.get(getKey));}輸出結果:
-------測試前準備-創建對象-------原對象map.get():100值相同的對象map.get():null-------測試結束-------

這里我們很明顯看到,我們要用之前的對象才能從HashMap中獲取到key 。這不是我們想要的效果?。?再也不頭鐵了 。
  • 這時有人就會說了,那我用之前的對象不就好了嘛,干嘛要new個新的對象呢?其實這也是demo和實際項目的差距 。如果你做過項目的就知道,我們的實體類每次請求進來都是個新的對象,最最最不濟 , 你什么PO轉DO,DO轉DTO不就是要創建新的對象嘛 。
吶吶吶,問題又來了 , 我不用Map不就行了 , 可以 。當然可以,但是現在的項目里面,如果說整個項目沒有用到Map的,那這個項目估計也不叫項目了,寫java的人,誰能拒絕來個Map呢 。
  • OK,最后啊,這hashcode()方法到底應該怎么寫呢,這也頭大啊 , 一個hashcode()咋就那么難呢,來來來思考兩分鐘,你想好怎么寫了嘛 。先別說 , 我把前提撩在這,你在仔細想想 。一個是 ”相同的對象“ 的 hashcode() 要相同,不同對象的 hashcode() 不能相同 。比如 :
Person key = new Person("1","張三");Person sameKey = new Person("1","張三");Person unSameKey = new Person("2","張三");
其中 key 和 sameKey 的 hashcode()一定要相同,因為我們的業務員就是認為他們應該是相同的 。但是同時 unSame 和 key 的 hashcode() 一定不能相同 。
  • 這個時候,使用Object的 hashcode() 很顯然就是不行了,應為它返回每個對象的 hashcode() 都是不相同的 。
  • 當然了 , 所有 hashcode() 都返回 0,這也不是不行,只是違背了這句話:如果兩個對象hashCode相同 , 不能證明兩個對象是同一個對象(不一定相等) 。
來吧,開始發散思維 。
方法一:
  • 使用String 的hashcode(),你對象里面的屬性不是String 類型嘛,String 里面已經重寫了 hashcod() 方法那我直接用現成的就好了 。
@Overridepublic int hashCode() {returnid.hashCode() + name.hashCode();}
但是我們還是要從各個角度出發去想一下的,比如判空,是否會值溢出之類的,這里我們也參考String 的 hashcode() 套娃一個 。優化后:
@Overridepublic int hashCode() {//初始值 , 別問為什么是 17, 因為我寫 18 你也會這樣問的int result = 17;// 31 作為基數,不知道啊,String 里面 的 hashcode() 就是寫的 31result = 31 * result + (id == null ? 0 : id.hashCode());result = 31 * result + (name == null ? 0 : name.hashCode());return result;}方法二:
  • 調用JDK提供好的Objects里面的hashcode()
@Overridepublic int hashCode() {return Objects.hash(id,name);}看了這兩個方法,沒錯,這都是站在巨人的肩膀上 。其實要是自己動手實現,那難度就直線上升了,至少算法這一塊要啃一下 。
hashcode() 方重寫規則
寫hashcode()要遵循以下原則:
equals不相等時并不強制要求哈希值相等 , 但是一個優秀的哈希算法應盡可能讓元素均勻分布,降低沖突發生的概率,即在equals不相等時盡量讓哈希值也不相等,這樣&&或||短路操作一旦生效,會極大提高程序的效率 。
重寫equals都是根據業務需求去進行重寫的,想想為什么String是這么判斷兩個字符串對象相等的 。如果拋開業務需求談這個,就是耍流氓 。但是大多數情況下,都是不用重寫 equals() 和 hashcode() 的,就是因為有了特殊的業務邏輯,所以我們才需要重寫里面的邏輯 。
知識盲區

推薦閱讀