當前位置:成語大全網 - 書法字典 - 如何在Java中正確實現hashCode方法

如何在Java中正確實現hashCode方法

在Java中正確實現hashCode方法;

相等和散列碼

平等意味著,壹般來說,哈希代碼更具技術性。如果我們難以理解,我們可以說他們僅通過壹個實現細節就提高了性能。

大多數數據結構使用equals方法來確定它們是否包含元素,例如:

列表& lt字符串& gtlist = arrays . as list(“a“,“b“,“c“);

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

這個變量contains的結果為true,因為盡管“b”是不同的實例(此外,忽略字符串的存在),但它們是相等的。

盡管整個類的數據結構得到了優化,這可以提高性能,但比較實例的每個元素然後將比較結果分配給contains是壹種浪費。

它們不是比較實例中包含的每個元素,而是使用壹種快速方法進行比較(減少潛在的實例相等性)。快速比較只需要比較以下幾個方面:

快捷比較意味著通過比較哈希值,它可以用整數值替換實例。具有相同哈希代碼的實例不壹定相等,但相等的實例必須具有相同的哈希值。(或者應該有,我們很快就會討論這壹點)這些數據結構通常由該技術命名,它們可以通過Hash來標識,其中HashMap是最著名的代表。

他們通常這樣工作。

當添加壹個元素時,其哈希代碼用於計算內部數組的索引(即所謂的bucket)。

如果是這樣的話,不相等的元素具有相同的哈希代碼,並且它們在同壹個桶上結束並綁定在壹起,例如通過添加到列表中。

當實例執行contains操作時,其哈希代碼將用於計算bucket值(索引值),並且只有當對應的索引值上存在元素時,才會比較實例。

因此,equals和hashCode是在Object類中定義的。

哈希的想法

如果hashcode被用作確定相等性的捷徑,那麽我們只需要關心壹件事:equal對象應該具有相同的hashCode,這就是為什麽如果我們重寫equals方法,我們必須創建壹個匹配的hashCode實現!

否則,相等的對象可能沒有相同的哈希代碼,因為它們將調用對象的默認實現。

哈希碼標準

引用官方文件

HashCode常規約定:

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

*根據equals(Object)方法,如果兩個對象相等,則調用hashCode方法的兩個對象必須產生相同的結果。

*根據equals(對象),方法是比較。如果兩個對象不相等,那麽兩個對象調用hashCode方法不壹定會產生不同的整數結果。但是,程序員應該知道,通過為不相等的對象產生不同的整數結果,可以提高哈希表的性能。

第壹點體現了等壹致性屬性,第二點是我們上面提出的要求。第三個解釋了壹個重要的細節,我們將在後面討論。

HashCode實現

下面是Person.hashCode的壹個非常簡單的實現

@覆蓋

public int hashCode(){

return Objects.hash(名字,姓氏);

}

Person通過合並多個字段來計算哈希代碼。由對象的哈希函數計算得出。

選擇字段

但是哪些領域是相關的呢?需求將幫助我們回答這個問題:如果相等的對象必須具有相同的哈希代碼,那麽計算哈希代碼時不應包括任何未用於相等性檢查的字段。(否則,這兩個對象只是在這些領域有所不同,但它們仍然可能是相等的。此時,兩個對象的哈希代碼將不同。)

當用於散列組字段應該相等時使用的字段子集。默認情況下,它們都使用相同的字段,但有壹些細節需要考慮。

壹致性

首先,有壹致性要求。應該挺嚴格的。雖然它允許相應的哈希代碼在某些字段發生變化時發生變化(這對於變量類來說是不可避免的),但哈希數據結構並不適合這種情況。

正如我們在上面看到的,哈希代碼用於確定元素的桶。但是,如果與哈希相關的字段發生變化,則不會重新計算哈希代碼,也不會更新內部數組。

這意味著未來通過equal對象甚至同壹個實例的查詢將失敗,並且數據結構計算的當前哈希代碼與之前存儲實例計算的哈希代碼不壹致,並且是壹個錯誤的存儲桶。

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

表演

哈希代碼最終計算的頻率與調用equals的頻率相似,因此這將是影響性能的關鍵部分,因此考慮這部分性能是非常有意義的。並且與equals相比,優化後有更大的改進空間。

除非使用非常復雜的算法或涉及大量字段,否則計算哈希碼的運算成本可以忽略不計,也是不可避免的。但是您還應該考慮是否需要包含操作的所有字段。集會需要特別警惕。例如,列表和集合將包含集合中的每個元素以計算哈希代碼。是否需要調用需要具體分析。

如果性能至關重要,使用Objects.hash可能不是最佳選擇,因為您需要為varargs創建壹個數組。但是通用規則優化是適用的:不要過早地使用通用哈希代碼算法,也許您需要放棄該集合,只有優化分析才能顯示潛在的改進。

碰撞

總是關註性能,那麽這個實現呢?

@覆蓋

public int hashCode(){

返回0;

}

快是肯定的。相等的對象將具有相同的哈希代碼。還有,沒有多變的領域!

但是我們之前談到的桶呢?!這樣,所有實例都將擁有相同的存儲桶!這將導致壹個包含所有元素的鏈表,其性能非常差。每次調用contains都會觸發對整個列表的線性掃描。

我們希望同壹個桶中的元素越少越好!即使是非常相似的對象,返回不同散列碼的算法也是壹個良好的開端。

如何實現上述效果部分取決於所選的字段。我們在計算中包含的細節越多,就越有可能獲得不同的哈希代碼。註:這個表現和我們說的完全相反。因此,有趣的是,使用過多或過少的字段都會導致性能低下。

防止沖突的另壹部分是使用實際計算散列的算法。

計算Hsah

計算字段的hashcode最簡單的方法是直接調用hashCode,組合將自動完成。常見的算法是與任意數量的數值(通常是基本數據類型)相乘,然後與字段哈希代碼相加。

int prime = 31;

int result = 1;

result = prime * result +((名字== null)?0:first name . hashcode());

result = prime * result+(last name = = null)?0:last name . hashcode());

返回結果;

這可能會導致溢出,但問題不大,因為它們不會生成Java異常。

請註意,由於輸入特定模式的數據,即使是非常好的哈希算法也可能導致頻繁的沖突。舉個簡單的例子,假設我們將通過增加點的x和y坐標來計算點的散布。當我們處理直線上的點f(x)=-x時,直線上的所有點都滿足:x+y == 0,並且會有很多碰撞。

但是,我們可以使用通用算法,只有當分析表明它不正確時,我們才需要修改哈希算法。

摘要

我們知道,計算散列碼是為了壓縮壹個相等的整數值:相等的對象必須具有相同的散列碼,為了提高性能,最好讓盡可能少的不相等的對象共享相同的散列碼。

這意味著如果您重寫equals方法,則必須重寫hashCode方法。

實現hashCode時

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

最好不要包含可變字段。

不要考慮對集合調用hashCode。

如果沒有特定於輸入的特殊模式,請嘗試使用通用哈希算法。

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