農林漁牧網

您現在的位置是:首頁 > 農業

Java基礎之hashcode剖析

2022-08-15由 Java鬥帝之路 發表于 農業

hashmap hashcode;hashcode是什麼

1。 前言

雜湊是計算機科學的一個基本概念。在 Java 中,高效的雜湊演算法支援一些最流行的集合,例如HashMap和HashSet,在本文中,我們將重點介紹hashCode() 的工作原理、它如何在集合中使用以及如何正確實現它。

2。 hashcode 原理

2。1 Java equals()和hashCode()的關係

Object。html#hashCode()

hashcode的理解

hashCode的存在主要是用於查詢的快捷性,如Hashtable,HashMap等,hashCode是用來在雜湊儲存結構中確定物件的儲存地址的;

如果兩個物件相同,就是適用於equals(java。lang。Object) 方法,那麼這兩個物件的hashCode一定要相同;

如果物件的equals方法被重寫,那麼物件的hashCode也儘量重寫,並且產生hashCode使用的物件,一定要和equals方法中使用的一致,否則就會違反上面提到的第2點;

兩個物件的hashCode相同,並不一定表示兩個物件就相同,也就是不一定適用於equals(java。lang。Object) 方法,只能夠說明這兩個物件在雜湊儲存結構中,如Hashtable,他們

“存放在同一個籃子裡”

再歸納一下就是

hashCode是用於查詢使用的

,而

equals是用於比較兩個物件的是否相等的

。以下這段話是從別人帖子回覆複製過來的,說得很形象:

(1) hashcode是用來查詢的,如果你學過資料結構就應該知道,在查詢和排序說過:假如記憶體中有這樣的位置 [0 1 2 3 4 5 6 7] 而我有個類,這個類有個欄位叫ID,我要把這個類存放在以上8個位置之一,如果不用hashcode而任意存放,那麼當查詢時就需要到這八個位置裡挨個去找,或者用類似二分法的演算法。 但如果用hashcode那就會使效率提高很多。

我們這個類中有個欄位叫ID,那麼我們就定義我們的hashcode為ID%8,然後把我們的類存放在取得得餘 數那個位置。比如我們的ID為9,9除8的餘數為1,那麼我們就把該類存在1這個位置,如果ID是13,求得 的餘數是5,那麼我們就把該類放在5這個位置。這樣,以後在查詢該類時就可以透過ID和8求餘數直接找到 存放的位置了。

(2) 但是如果兩個類有相同的hashcode該怎麼辦呢(假設上面的ID不是唯一的),假如 9%8=1,17%8=1,那麼這是不是合法的呢?回答是:可以這樣。

那麼如何判斷呢?在這個時候就需要定義 equals了。也就是說,我們先透過hashcode來判斷兩個類是否存放在一個桶裡面,但是這個桶裡面可以有很多類,那麼我們就需要透過equals 來在這個桶裡找到我們要的類。

那麼。重寫了equals(),為什麼還要重寫hashCode()呢?

想想,你要在一個桶裡找東西,你必須先要找到這個桶啊,你不透過重寫hashcode()來找到桶,光重寫equals()有什麼用啊

2。2 舉例分析

package com。wxw。common。hashcode;import java。util。HashSet;import java。util。Set;/** * @author 公眾號:Java半顆糖 * @desc: * @date: 2021/7/24 */public class DemoHashCode { private int id; public void setId(Integer id) { this。id = id; } public Integer getId() { return id; } @Override public int hashCode() { return id % 10; } public static void main(String[] args) { DemoHashCode a = new DemoHashCode(); DemoHashCode b = new DemoHashCode(); a。setId(1); b。setId(1); Set set = new HashSet<>(); set。add(a); set。add(b); System。out。println(a。hashCode() == b。hashCode()); System。out。println(a。equals(b)); System。out。println(set); /** * —————— * 執行結果: * true * false * [com。wxw。common。hashcode。DemoHashCode@1, com。wxw。common。hashcode。DemoHashCode@1] */ }}複製程式碼

以上這個示例,我們只重寫了hashcode() 方法,從上面的結果可以看出,雖然兩個物件的hashcode相等,但實際上兩個物件並不相等。

我們沒有重寫 equals()方法,那麼就會呼叫Object預設的equals()方法,是比較兩個物件的引用是不是相同,實際上兩個物件的引用肯定是不等的,這裡我們將生成的物件放到了HashSet中,而HashSet中只能夠存放唯一的物件,也就是相同的(適用於equals方法)的物件只會存放一個,但是這裡實際上是兩個物件a,b都被放到了HashSet中,這樣HashSet就失去了他本身的意義了。 此時我們把equals方法給加上:

package com。wxw。common。hashcode;import java。util。HashSet;import java。util。Set;/** * @author 公眾號:Java半顆糖 * @desc: * @date: 2021/7/24 */public class DemoHashCode { private int id; public void setId(Integer id) { this。id = id; } public Integer getId() { return id; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o。getClass()) return false; DemoHashCode that = (DemoHashCode) o; return id == that。id; } @Override public int hashCode() { return id % 10; } public static void main(String[] args) { DemoHashCode a = new DemoHashCode(); DemoHashCode b = new DemoHashCode(); a。setId(1); b。setId(1); Set set = new HashSet<>(); set。add(a); set。add(b); System。out。println(a。hashCode() == b。hashCode()); System。out。println(a。equals(b)); System。out。println(set); /** * —————— * 執行結果: * true * true * [com。wxw。common。hashcode。DemoHashCode@1] */ }}複製程式碼

從結果我們可以看出,現在兩個物件就完全相等了,HashSet中也只存放了一份物件。

3。 hash 衝突

雜湊表的內在行為也帶來了相應的問題:即使使用有效的雜湊演算法,兩個或多個物件可能具有相同的雜湊碼,即使兩個物件不相等。因此,即使它們具有不同的雜湊值,它們的雜湊碼也會指向同一個桶。 這種情況通常被稱為雜湊衝突。

解決hash衝突的方法,詳細分析可以點此處檢視:

連結串列法

開放定址法

Java中的hashMap是使用連結串列法解決hash衝突的

當兩個或多個物件指向同一個儲存桶時,它們只是儲存在一個連結串列中。在這種情況下,雜湊表是一個連結串列陣列,每個具有相同雜湊值的物件都附加到連結串列中的通索引處。

Java基礎之hashcode剖析

在最壞的情況下,幾個桶會繫結一個連結串列,而對連結串列中物件的檢索將是線性執行的。

處理雜湊衝突 簡言之,為什麼高效地實現 hashCode()如此重要?

Java8 也為HashMap的實現進行了增強,如果桶大小超過8,節點入超過64,則會轉換為紅黑樹,而不是使用連結串列,這樣當連結串列太長接近線性查詢(複雜度為O(n))時 用紅黑樹 O(logN) 代替。

3。1 hashmap和hashcode的聯絡

User類中我們重寫hashcode方法

@Datapublic class User { private long userId; private String userName; private String email; @Override public int hashCode() { int hash = 7; hash = 31 * hash + (int) userId; hash = 31 * hash + (userName == null ? 0 : userName。hashCode()); hash = 31 * hash + (email == null ? 0 : email。hashCode()); System。out。println(“hashCode() called - Computed hash: ” + hash); return hash; } public User(Long userId, String userName, String email) { this。userId = userId; this。userName = userName; this。email = email; }}複製程式碼

應用程式的入口:

public class DemoHashMap { public static void main(String[] args) { Map users = new HashMap<>(); User user1 = new User(1L, “John”, “john@domain。com”); User user2 = new User(2L, “Jennifer”, “jennifer@domain。com”); User user3 = new User(3L, “Mary”, “mary@domain。com”); users。put(user1, user1); users。put(user2, user2); users。put(user3, user3); if (users。containsKey(user1)) { System。out。print(“User found in the collection”); } }}複製程式碼

在這裡,重要的是要注意,每次將物件儲存在雜湊對映中並使用

containsKey()

方法檢查時,都會呼叫

hashCode()

並將計算出的雜湊碼列印到控制檯:

Java基礎之hashcode剖析

結論

很明顯,生成高效的

hashCode()

實現通常需要混合一些數學概念(即素數和任意數)、邏輯和基本數學運算。但是我們也可以有效地實現

hashCode(),只需要確保雜湊演算法為不相等的物件生成不同的雜湊碼,並且它與

equals()* 的實現一致。