一级日韩免费大片,亚洲一区二区三区高清,性欧美乱妇高清come,久久婷婷国产麻豆91天堂,亚洲av无码a片在线观看

java語(yǔ)言

如何正確實(shí)現Java中的hashCode方法

時(shí)間:2025-03-27 10:02:22 java語(yǔ)言 我要投稿
  • 相關(guān)推薦

如何正確實(shí)現Java中的hashCode方法

  導語(yǔ):hashCode是jdk根據對象的地址或者字符串或者數字算出來(lái)的int類(lèi)型的數值,那么如何正確實(shí)現Java中的hashCode方法呢?一起來(lái)學(xué)習下吧:

  相等和哈希碼

  相等是從一般的方面來(lái)講,哈希碼更加具有技術(shù)性。如果我們在理解方面存在困難,我們可以說(shuō),他們通過(guò)只是一個(gè)實(shí)現細節來(lái)提高了性能。

  大多數的數據結構通過(guò)equals方法來(lái)判斷他們是否包含一個(gè)元素,例如:

  Listlist = Arrays.asList("a", "b", "c");

  boolean contains = list.contains("b");

  這個(gè)變量contains結果是true,因為,雖然”b”是不相同的實(shí)例(此外,忽略字符串駐留),但是他們是相等的。

  通過(guò)比較實(shí)例的每個(gè)元素,然后將比較結果賦值給contains是比較浪費的,雖然整個(gè)類(lèi)的數據結構進(jìn)行了優(yōu)化,能夠提升性能。

  他們通過(guò)使用一種快捷的方式(減少潛在的實(shí)例相等)進(jìn)行比較,從而代替通過(guò)比較實(shí)例所包含的每個(gè)元素。而快捷比較僅需要比較下面這些方面:

  快捷方式比較即通過(guò)比較哈希值,它可以將一個(gè)實(shí)例用一個(gè)整數值來(lái)代替。哈希碼相同的實(shí)例不一定相等,但相等的實(shí)例一定具有有相同的哈希值。(或應該有,我們很快就會(huì )討論這個(gè))這些數據結構經(jīng)常通過(guò)這種這種技術(shù)來(lái)命名,可以通過(guò)Hash來(lái)識別他們的,其中,HashMap是其中最著(zhù)名的代表。

  它們通常是這樣這樣運作的

  當添加一個(gè)元素,它的哈希碼是用來(lái)計算內部數組的索引(即所謂的桶)

  如果是,不相等的元素有相同的哈希碼,他們最終在同一個(gè)桶上并且捆綁在一起,例如通過(guò)添加到列表。

  當一個(gè)實(shí)例來(lái)進(jìn)行contains操作時(shí),它的哈希碼將用來(lái)計算桶值(索引值),只有當對應索引值上存在元素時(shí),才會(huì )對實(shí)例進(jìn)行比較。

  因此equals,hashCode是定義在Object類(lèi)中。

  散列法的思想

  如果hashCode作為快捷方式來(lái)確定相等,那么只有一件事我們應該關(guān)心:相等的對象應該具有相同的哈希碼,這也是為什么如果我們重寫(xiě)了equals方法后,我們必須創(chuàng )建一個(gè)與之匹配的hashCode實(shí)現的原因!

  否則相等的對象是可能不會(huì )有相同的哈希碼的,因為它們將調用的是Object's的默認實(shí)現。

  HashCode 準則

  引用自官方文檔

  hashCode通用約定:

  * 調用運行Java應用程序中的同一對象,hashCode方法必須始終返回相同的整數。這個(gè)整數不需要在不同的Java應用程序中保持一致。

  * 根據equals(Object)的方法來(lái)比較,如果兩個(gè)對象是相等的,兩個(gè)對象調用hashCode方法必須產(chǎn)生相同的結果。

  * 根據equals(Object)的方法是比較,如果兩個(gè)對象是不相等的,那么兩個(gè)對象調用hashCode方法并不一定產(chǎn)生不同的整數的結果。但是,程序員應該意識到給不相等的對象產(chǎn)生不同的整數結果將有可能提高哈希表的性能。

  第一點(diǎn)反映出了相等的一致性屬性,第二個(gè)就是我們上面提出的要求。第三個(gè)闡述了一個(gè)重要的細節,我們將在稍后討論。

  HashCode實(shí)現

  下面是非常簡(jiǎn)單的Person.hashCode的實(shí)現

  @Override

  public int hashCode() {

  return Objects.hash(firstName, lastName);

  }

  person’s是通過(guò)多個(gè)字段結合來(lái)計算哈希碼的。都是通過(guò)Object的hash函數來(lái)計算。

  選擇字段

  但哪些字段是相關(guān)的嗎?需求將會(huì )幫助我們回答這個(gè)問(wèn)題:如果相等的對象必須具有相同的哈希碼,那么計算哈希碼就不應包括任何不用于相等檢查的字段。(否則兩個(gè)對象只是這些字段不同但是仍然有可能會(huì )相等,此時(shí)他們這兩個(gè)對象哈希碼卻會(huì )不相同。)

  所以用于哈希組字段應該相等時(shí)使用的字段的子集。默認情況下都使用相同的字段,但有一些細節需要考慮。

  一致性

  首先,有一致性的要求。它應該相當嚴格。雖然它允許如果一些字段改變對應的哈希碼發(fā)生變化(對于可變的類(lèi)是不可避免的),但是哈希數據結構并不是為這種場(chǎng)景準備的。

  正如我們以上所見(jiàn)的哈希碼用于確定元素的桶。但如果hash-relevant字段發(fā)生了改變,并不會(huì )重新計算哈希碼、也不會(huì )更新內部數組。

  這意味著(zhù)以后通過(guò)相等的對象,甚至同一實(shí)例進(jìn)行查詢(xún)也會(huì )失敗,數據結構計算當前的哈希碼與之前存儲實(shí)例計算的哈希碼并不一致,并是錯誤的桶。

  結論:最好不要使用可變字段計算哈希碼!

  性能

  哈希碼最終計算的頻率與可能調用equals差不多,那么這里將是影響性能的關(guān)鍵部分,因此考慮此部分性能也是非常有意義的。并且與equals相比,優(yōu)化之后又更大的上升空間。

  除非使用非常復雜的算法或者涉及非常多的字段,那么計算哈希碼的運算成本是微不足道的、同樣也是不可避免的。但是也應該考慮是否需要包含所有的字段來(lái)進(jìn)行運算。集合需要特別警惕的對待。以L(fǎng)ists和sets為例,將會(huì )包含集合里面的每一個(gè)元素來(lái)計算哈希碼。是否需要調用它們需要具體情況具體分析。

  如果性能是至關(guān)重要的,使用Objects.hash因為需要為varargs創(chuàng )建一個(gè)數組也許并不是最好的選擇。但一般規則優(yōu)化是適用的:不要過(guò)早地使用一個(gè)通用的散列碼算法,也許需要放棄集合,只有優(yōu)化分析顯示潛在的改進(jìn)。

  碰撞

  總是關(guān)注性能,這個(gè)實(shí)現怎么呢?

  @Override

  public int hashCode() {

  return 0;

  }

  快是肯定的。相等的對象將具有相同的哈希碼。并且,沒(méi)有可變的字段!

  但是,我們之前說(shuō)過(guò)的桶呢?!這種方式下所有的實(shí)例將會(huì )有相同的桶!這將會(huì )導致一個(gè)鏈表來(lái)包含所有的元素,這樣一來(lái)將會(huì )有非常差的性能。每次調用contains將會(huì )觸發(fā)對整個(gè)list線(xiàn)性?huà)呙琛?/p>

  我們希望盡可能少的元素在同一個(gè)桶!一個(gè)算法返回變化多端的哈希碼,即使對于非常相似的對象,是一個(gè)好的開(kāi)始。

  怎樣才能達到上面的效果部分取決于選取的字段,我們在計算中包含更多的細節,越有可能獲取到不同的哈希碼。注意:這個(gè)與我們所說(shuō)的性能是完全相反的。因此,有趣的是,使用過(guò)多或者過(guò)少的字段都會(huì )導致糟糕的性能。

  防止碰撞的另一部分是使用實(shí)際計算散列的算法。

  計算Hsah

  最簡(jiǎn)單的方法來(lái)計算一個(gè)字段的哈希碼是通過(guò)直接調用hashCode,結合的話(huà)會(huì )自動(dòng)完成。常見(jiàn)的算法是首先在以任意數量的數值(通常是基本數據類(lèi)型)反復進(jìn)行相乘操作再與字段哈希碼相加

  int prime = 31;

  int result = 1;

  result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());

  result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());

  return result;

  這可能導致溢出,但是不是特別有問(wèn)題的,因為他們并沒(méi)有產(chǎn)生Java異常。

  注意,即使是非常良好的的哈希算法也可能因為輸入特定的模式的數據有導致頻繁碰撞。作為一個(gè)簡(jiǎn)單的例子假設我們會(huì )計算點(diǎn)的散列通過(guò)增加他們的x和y坐標。當我們處理f(x) = -x線(xiàn)上的點(diǎn)時(shí),線(xiàn)上的點(diǎn)都滿(mǎn)足:x + y == 0,將會(huì )有大量的碰撞。

  但是:我們可以使用一個(gè)通用的算法,只到分析表明并不正確,才需要對哈希算法進(jìn)行修改。

  總結

  我們了解到計算哈希碼就是壓縮相等的一個(gè)整數值:相等的對象必須有相同的哈希碼,而出于對性能的考慮:最好是盡可能少的不相等的對象共享相同的哈希碼。

  這就意味著(zhù)如果重寫(xiě)了equals方法,那么就必須重寫(xiě)hashCode方法

  當實(shí)現hashCode

  使用與equals中使用的相同的字段(或者equals中使用字段的子集)

  最好不要包含可變的字段。

  對集合不要考慮調用hashCode

  如果沒(méi)有特殊的輸入特定的模式,盡量采用通用的哈希算法

  記住hashCode性能,所以除非分析表明必要性,否則不要浪費太多的精力。


【如何正確實(shí)現Java中的hashCode方法】相關(guān)文章:

java中的hashCode小例子教程05-23

Java中如何實(shí)現顯示動(dòng)態(tài)的時(shí)間03-14

如何在java中實(shí)現左右鍵菜單03-20

Java實(shí)現多線(xiàn)程的方法04-15

關(guān)于Java動(dòng)態(tài)實(shí)現的方法04-20

JAVA實(shí)現生成GUID的方法06-02

如何正確使用Java數組04-29

實(shí)現java屏幕抓屏的方法11-29

從Java的jar文件中如何讀取數據的方法01-24

一级日韩免费大片,亚洲一区二区三区高清,性欧美乱妇高清come,久久婷婷国产麻豆91天堂,亚洲av无码a片在线观看