你知道一個物件的唯一標誌不能只寫一個漂亮的equals來實作
太棒了,不過現在你也必須實作hashCode方法。
##相等和哈希碼相等是從一般的方面來講,哈希碼更加具有技術性。來提高了效能。 ,雖然”b」是不相同的實例(此外,忽略字串駐留),但是他們是相等的。HashMap
是其中最著名的代表。當增加一個元素,它的雜湊碼是用來計算內部陣列
的索引(即所謂的桶)
如果是,不相等的元素有相同的哈希碼,他們最終在同一個桶上並且捆綁在一起,例如通過添加到列表。 contains
操作時,它的雜湊碼將用來計算桶值(索引值),只有當對應索引值上存在元素時,才會對實例進行比較。 ##因此
,
hashCode是定義在Object
類別中。
hashCode
作為快捷方式來確定相等,那麼只有一件事我們應該關心:相等的物件應該具有相同的雜湊碼,這也是為什麼如果我們重寫了hashCode實現的原因!否則相等的物件是可能不會有相同的雜湊碼的,因為它們將呼叫的是Object's
的預設實作。HashCode
準則引用自官方文件
* 根據equals(Object)的方法來比較,如果兩個物件是相等的,兩個物件呼叫hashCode方法必須產生相同的結果。 * 根據equals(Object)的方法是比較,如果兩個物件是不相等的,那麼兩個物件呼叫hashCode方法並不一定會產生不同的整數的結果。但是,
程式設計師應該意識到給不相等的物件產生不同的整數結果將有可能提高雜湊表的效能。
<a href="http://www.php.cn/wiki/60.html" target="_blank">第一點反映出了相等的一致性</a>屬性
,第二個就是我們上面提出的要求。第三個闡述了一個重要的細節,我們稍後再討論。
下面是非常簡單的Person.hashCode
的實作<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:java;toolbar:false;">List<String> list = Arrays.asList("a", "b", "c");
boolean contains = list.contains("b");</pre><div class="contentsignin">登入後複製</div></div>
person's是透過多個欄位結合來計算雜湊碼的。都是透過Object
的
函數
來計算。
但哪些欄位是相關的嗎?需求將會幫助我們回答這個問題:如果相等的物件必須具有相同的雜湊碼,那麼計算雜湊碼就不應包括任何不用於相等檢查的欄位。 (否則兩個物件只是這些字段不同但是仍然有可能會相等,此時他們這兩個物件哈希碼卻會不相同。)所以用於哈希組字段應該相等時使用的字段的子集。預設情況下都使用相同的字段,但有一些細節需要考慮。 首先,有一致性的要求。它应该相当严格。虽然它允许如果一些字段改变对应的哈希码发生变化(对于可变的类是不可避免的),但是哈希数据结构并不是为这种场景准备的。 正如我们以上所见的哈希码用于确定元素的桶。但如果hash-relevant字段发生了改变,并不会重新计算哈希码、也不会更新内部数组。 这意味着以后通过相等的对象,甚至同一实例进行查询也会失败,数据结构计算当前的哈希码与之前存储实例计算的哈希码并不一致,并是错误的桶。 结论:最好不要使用可变字段计算哈希码! 哈希码最终计算的频率与可能调用 除非使用非常复杂的算法或者涉及非常多的字段,那么计算哈希码的运算成本是微不足道的、同样也是不可避免的。但是也应该考虑是否需要包含所有的字段来进行运算。集合需要特别警惕的对待。以 如果性能是至关重要的,使用 总是关注性能,这个实现怎么呢? 快是肯定的。相等的对象将具有相同的哈希码。并且,没有可变的字段! 但是,我们之前说过的桶呢?!这种方式下所有的实例将会有相同的桶!这将会导致一个链表来包含所有的元素,这样一来将会有非常差的性能。每次调用 我们希望尽可能少的元素在同一个桶!一个算法返回变化多端的哈希码,即使对于非常相似的对象,是一个好的开始。 怎样才能达到上面的效果部分取决于选取的字段,我们在计算中包含更多的细节,越有可能获取到不同的哈希码。注意:这个与我们所说的性能是完全相反的。因此,有趣的是,使用过多或者过少的字段都会导致糟糕的性能。 防止碰撞的另一部分是使用实际计算散列的算法。 最简单的方法来计算一个字段的哈希码是通过直接调用 这可能导致溢出,但是不是特别有问题的,因为他们并没有产生Java异常。 注意,即使是非常良好的的哈希算法也可能因为输入特定的模式的数据有导致频繁碰撞。作为一个简单的例子假设我们会计算点的散列通过增加他们的x和y坐标。当我们处理 但是:我们可以使用一个通用的算法,只到分析表明并不正确,才需要对哈希算法进行修改。 我们了解到计算哈希码就是压缩相等的一个整数值:相等的对象必须有相同的哈希码,而出于对性能的考虑:最好是尽可能少的不相等的对象共享相同的哈希码。 这就意味着如果重写了 当实现 使用与equals中使用的相同的字段(或者equals中使用字段的子集) 最好不要包含可变的字段。 对集合不要考虑调用 如果没有特殊的输入特定的模式,尽量采用通用的哈希算法 记住一致性
性能
equals
差不多,那么这里将是影响性能的关键部分,因此考虑此部分性能也是非常有意义的。并且与equals
相比,优化之后又更大的上升空间。Lists
和sets
为例,将会包含集合里面的每一个元素来计算哈希码。是否需要调用它们需要具体情况具体分析。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;
f(x) = -x
线上的点时,线上的点都满足:x + y == 0
,将会有大量的碰撞。总结
equals
方法,那么就必须重写hashCode
方法hashCode
hashCode
hashCode
性能,所以除非分析表明必要性,否则不要浪费太多的精力。
以上是Java實作hashCode方法的範例程式碼分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!