天天看點

面試官:HashSet如何保證元素不重複?

本文已收錄《Java常見面試題》系列,Git 開源位址:https://gitee.com/mydb/interview

HashSet 實作了 Set 接口,由哈希表(實際是 HashMap)提供支援。HashSet 不保證集合的疊代順序,但允許插入 null 值。也就是說 HashSet 不能保證元素插入順序和疊代順序相同。

HashSet 具備去重的特性,也就是說它可以将集合中的重複元素自動過濾掉,保證存儲在 HashSet 中的元素都是唯一的。

HashSet 基本操作方法有:add(添加)、remove(删除)、contains(判斷某個元素是否存在)和 size(集合數量)。這些方法的性能都是固定操作時間,如果哈希函數是将元素分散在桶中的正确位置。

HashSet 基本使用如下:

HashSet 不能保證插入元素的順序和循環輸出元素的順序一定相同,也就是說 HashSet 其實是無序的集合,具體代碼示例如下:

以上程式的執行結果如下:

從上述代碼和執行結果可以看出,HashSet 插入的順序是:深圳 -> 北京 -> 西安,而循環列印的順序卻是:西安 -> 深圳 -> 北京,是以 HashSet 是無序的,不能保證插入和疊代的順序一緻。

PS:如果要保證插入順序和疊代順序一緻,可使用 LinkedHashSet 來替換 HashSet。

有人說 HashSet 隻能保證基礎資料類型不重複,卻不能保證自定義對象不重複?這樣說對嗎?

我們通過以下示例來說明此問題。

使用 HashSet 存儲基本資料類型,實作代碼如下:

從上述結果可以看出,使用 HashSet 可以保證基礎資料類型不重複。

接下來,将自定義對象存儲到 HashSet 中,實作代碼如下:

從上述結果可以看出,自定義對象類型确實沒有被去重,那也就是說 HashSet 不能實作自定義對象類型的去重咯?

其實并不是,HashSet 去重功能是依賴元素的 hashCode 和 equals 方法判斷的,通過這兩個方法傳回的都是 true 那就是相同對象,否則就是不同對象。而前面的 Long 類型元素之是以能實作去重,正是因為 Long 類型中已經重寫了 hashCode 和 equals 方法,具體實作源碼如下:

更多關于 hashCode 和 equals 的内容,詳見:https://mp.weixin.qq.com/s/40zaEJEkQYM3Awk2EwIrWA

那麼,想讓 HashSet 支援自定義對象去重,隻需要在自定義對象中重寫 hashCode 和 equals 方法即可,具體實作代碼如下:

重新運作以上代碼,執行結果如下圖所示:

從上述結果可以看出,之前的重複項“曹操”已經被去重了。

我們隻要了解了 HashSet 執行添加元素的流程,就能知道為什麼 HashSet 能保證元素不重複了?

HashSet 添加元素的執行流程是:當把對象加入 HashSet 時,HashSet 會先計算對象的 hashcode 值來判斷對象加入的位置,同時也會與其他加入的對象的 hashcode 值作比較,如果沒有相符的 hashcode,HashSet 會假設對象沒有重複出現,會将對象插入到相應的位置中。但是如果發現有相同 hashcode 值的對象,這時會調用對象的 equals() 方法來檢查對象是否真的相同,如果相同,則 HashSet 就不會讓重複的對象加入到 HashSet 中,這樣就保證了元素的不重複。

為了更清楚的了解 HashSet 的添加流程,我們可以嘗試閱讀 HashSet 的具體實作源碼,HashSet 添加方法的實作源碼如下(以下源碼基于 JDK 8):

從上述源碼可以看出 HashSet 中的 add 方法,實際調用的是 HashMap 中的 put,那麼我們繼續看 HashMap 中的 put 實作:

從上述源碼可以看出,HashMap 中的 put() 方法又調用了 putVal() 方法,putVal() 的源碼如下:

從上述源碼可以看出,當将一個鍵值對放入 HashMap 時,首先根據 key 的 hashCode() 傳回值決定該 Entry 的存儲位置。如果有兩個 key 的 hash 值相同,則會判斷這兩個元素 key 的 equals() 是否相同,如果相同就傳回 true,說明是重複鍵值對,那麼 HashSet 中 add() 方法的傳回值會是 false,表示 HashSet 添加元素失敗。是以,如果向 HashSet 中添加一個已經存在的元素,新添加的集合元素不會覆寫已有元素,進而保證了元素的不重複。如果不是重複元素,put 方法最終會傳回 null,傳遞到 HashSet 的 add 方法就是添加成功。

HashSet 底層是由 HashMap 實作的,它可以實作重複元素的去重功能,如果存儲的是自定義對象必須重寫 hashCode 和 equals 方法。HashSet 保證元素不重複是利用 HashMap 的 put 方法實作的,在存儲之前先根據 key 的 hashCode 和 equals 判斷是否已存在,如果存在就不在重複插入了,這樣就保證了元素的不重複。

卒然臨之而不驚,無故加之而不怒。 部落客:80 後程式員。愛好:讀書、寫作和慢跑。 公衆号:Java面試真題解析

關注下面二維碼,訂閱更多精彩内容。

面試官:HashSet如何保證元素不重複?
面試官:HashSet如何保證元素不重複?
面試官:HashSet如何保證元素不重複?

關注公衆号(加好友):

面試官:HashSet如何保證元素不重複?

作者:

王磊的部落格

出處:

http://vipstone.cnblogs.com/