首頁 > Java > java教程 > 主體

Java實作hashCode方法的範例程式碼分享

黄舟
發布: 2017-03-28 10:47:52
原創
1855 人瀏覽過

你知道一個物件的唯一標誌不能只寫一個漂亮的equals來實作

太棒了,不過現在你也必須實作hashCode方法。

##相等和哈希碼

相等是從一般的方面來講,哈希碼更加具有技術性。來提高了效能。 ,雖然”b」是不相同的實例(此外,忽略字串駐留),但是他們是相等的。

是比較浪費的,雖然整個類別的資料結構進行了最佳化,能夠提升效能。實例所包含的每個元素。的實例不一定相等,但相等的實例一定具有有相同的哈希值。 Hash

來識別他們的,其中,

HashMap

是其中最著名的代表。當增加一個元素,它的雜湊碼是用來計算內部

陣列

索引(即所謂的桶)

如果是,不相等的元素有相同的哈希碼,他們最終在同一個桶上並且捆綁在一起,例如通過添加到列表。 contains操作時,它的雜湊碼將用來計算桶值(索引值),只有當對應索引值上存在元素時,才會對實例進行比較。 ##因此

equals

hashCode

是定義在Object類別中。

#如果

hashCode

作為快捷方式來確定相等,那麼只有一件事我們應該關心:相等的物件應該具有相同的雜湊碼,這也是為什麼如果我們重寫了* 呼叫執行Java應用程式中的相同物件,hashCode方法必須始終傳回相同的整數。這個整數不需要在不同的Java應用程式中保持一致。

* 根據equals(Object)的方法來比較,如果兩個物件是相等的,兩個物件呼叫hashCode方法必須產生相同的結果。 * 根據equals(Object)的方法是比較,如果兩個物件是不相等的,那麼兩個物件呼叫hashCode方法並不一定會產生不同的整數的結果。但是,程式設計師應該意識到給不相等的物件產生不同的整數結果將有可能提高雜湊表的效能。 <a href="http://www.php.cn/wiki/60.html" target="_blank">第一點反映出了相等的一致性</a>屬性,第二個就是我們上面提出的要求。第三個闡述了一個重要的細節,我們稍後再討論。

HashCode實作

下面是非常簡單的Person.hashCode的實作<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:java;toolbar:false;">List&lt;String&gt; list = Arrays.asList(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;); boolean contains = list.contains(&quot;b&quot;);</pre><div class="contentsignin">登入後複製</div></div>person's是透過多個欄位結合來計算雜湊碼的。都是透過Object

hash

函數來計算。

選擇欄位但哪些欄位是相關的嗎?需求將會幫助我們回答這個問題:如果相等的物件必須具有相同的雜湊碼,那麼計算雜湊碼就不應包括任何不用於相等檢查的欄位。 (否則兩個物件只是這些字段不同但是仍然有可能會相等,此時他們這兩個物件哈希碼卻會不相同。)

所以用於哈希組字段應該相等時使用的字段的子集。預設情況下都使用相同的字段,但有一些細節需要考慮。

一致性

首先,有一致性的要求。它应该相当严格。虽然它允许如果一些字段改变对应的哈希码发生变化(对于可变的类是不可避免的),但是哈希数据结构并不是为这种场景准备的。

正如我们以上所见的哈希码用于确定元素的桶。但如果hash-relevant字段发生了改变,并不会重新计算哈希码、也不会更新内部数组。

这意味着以后通过相等的对象,甚至同一实例进行查询也会失败,数据结构计算当前的哈希码与之前存储实例计算的哈希码并不一致,并是错误的桶。

结论:最好不要使用可变字段计算哈希码!

性能

哈希码最终计算的频率与可能调用equals差不多,那么这里将是影响性能的关键部分,因此考虑此部分性能也是非常有意义的。并且与equals相比,优化之后又更大的上升空间。

除非使用非常复杂的算法或者涉及非常多的字段,那么计算哈希码的运算成本是微不足道的、同样也是不可避免的。但是也应该考虑是否需要包含所有的字段来进行运算。集合需要特别警惕的对待。以Listssets为例,将会包含集合里面的每一个元素来计算哈希码。是否需要调用它们需要具体情况具体分析。

如果性能是至关重要的,使用Objects.hash因为需要为varargs创建一个数组也许并不是最好的选择。但一般规则优化是适用的:不要过早地使用一个通用的散列码算法,也许需要放弃集合,只有优化分析显示潜在的改进。

碰撞

总是关注性能,这个实现怎么呢?

@Override
public int hashCode() {
    return 0;
}
登入後複製

快是肯定的。相等的对象将具有相同的哈希码。并且,没有可变的字段!

但是,我们之前说过的桶呢?!这种方式下所有的实例将会有相同的桶!这将会导致一个链表来包含所有的元素,这样一来将会有非常差的性能。每次调用contains将会触发对整个list线性扫描。

我们希望尽可能少的元素在同一个桶!一个算法返回变化多端的哈希码,即使对于非常相似的对象,是一个好的开始。

怎样才能达到上面的效果部分取决于选取的字段,我们在计算中包含更多的细节,越有可能获取到不同的哈希码。注意:这个与我们所说的性能是完全相反的。因此,有趣的是,使用过多或者过少的字段都会导致糟糕的性能。

防止碰撞的另一部分是使用实际计算散列的算法。

计算Hsah

最简单的方法来计算一个字段的哈希码是通过直接调用hashCode,结合的话会自动完成。常见的算法是首先在以任意数量的数值(通常是基本数据类型)反复进行相乘操作再与字段哈希码相加

int prime = 31;
int result = 1;
result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
return result;
登入後複製

这可能导致溢出,但是不是特别有问题的,因为他们并没有产生Java异常。

注意,即使是非常良好的的哈希算法也可能因为输入特定的模式的数据有导致频繁碰撞。作为一个简单的例子假设我们会计算点的散列通过增加他们的x和y坐标。当我们处理f(x) = -x线上的点时,线上的点都满足:x + y == 0,将会有大量的碰撞。

但是:我们可以使用一个通用的算法,只到分析表明并不正确,才需要对哈希算法进行修改。

总结

我们了解到计算哈希码就是压缩相等的一个整数值:相等的对象必须有相同的哈希码,而出于对性能的考虑:最好是尽可能少的不相等的对象共享相同的哈希码。

这就意味着如果重写了equals方法,那么就必须重写hashCode方法

当实现hashCode

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

  • 最好不要包含可变的字段。

  • 对集合不要考虑调用hashCode

  • 如果没有特殊的输入特定的模式,尽量采用通用的哈希算法

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


以上是Java實作hashCode方法的範例程式碼分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板