HashSet如何保證元素不重複 面試必問

2022-09-24 09:21:09 字數 4102 閱讀 9883

目錄

hashset 實現了 set 介面,由雜湊表(實際是 hashmap)提供支援。hashset 不保證集合的迭代順序,但允許插入 null 值。也就是說 hashset 不能保證元素插入順序和迭代順序相同。

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

hashset 基本操作方法有:add(新增)、remove(刪除)、contains(判斷某個元素是否存在)和 size(集合數量)。這些方法的效能都是固定操作時間,如果雜湊函式是將元素分散在桶中的正確位置。

hashset 基本使用如下:

// 建立 hashset 集合

hashset strset = new hashset<>();

// 給 hashset 新增資料

strset.add("j**a");

strset.add("mysql");

strset.add("redis");

// 迴圈列印 hashset 中的所有元素

strset.foreach(s -> system.out.println(s));

hashset 不能保證插入元素的順序和迴圈輸出元素的順序一定相同,也就是說 hashset 其實是無序的集合,具體**示例如下:

hashset mapset = new hashset<>();

mapset.add("深圳");

mapset.add("北京");

mapset.add("西安");

// 迴圈列印 hashset 中的所有元素

mapset.foreach(m -> system.out.println(m));

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

從上述**和執行結果可以看出,hashset 插入的順序是:深圳 -> 北京 -> 西安,而迴圈列印的順序卻是:西安 -> 深圳 -> 北京,所以 hashset 是無序的,不能保證插入和迭代的順序一致。

ps:如果要保證插入順序和迭代順序一致,可使用 linkedhashset 來替換 hashset。

有人說 hashset 只能保證基礎資料型別不重複,卻不能保證自定義物件不重複?這樣說對嗎?

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

使用 hashset 儲存基本資料型別,實現**如下:

hashset longset = new hashset<>();

longset.add(666l);

longset.add(777l);

longset.add(999l);

longset.add(666l);

// 迴圈列印 hashset 中的所有元素

longset.foreach(l -> system.out.println(l));

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

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

接下來,將自定義物件儲存到 hashset 中,實現**如下:

public class hashsetexample

}@getter

@setter

@tostring

class person

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

從上述結果可以看出,自定義物件型別確實沒有被去重,那也就是說 hashset 不能實現自定義物件型別的去重咯?

其實並不是,hashset 去重功能是依賴元素的 hashcode 和 equals 方法判斷的,通過這兩個方法返回的都是 true 那就是相同物件,否則就是不同物件。而前面的 long 型別元素之所以能實現去重,正是因為 long 型別中已經重寫了 hashcode 和 equals 方法,具體實現原始碼如下:

@override

public int hashcode()

public boolean equals(object obj)

return false;

}//省略其他原始碼......

更多關於 hashcode 和 equals 的內容,詳見:

那麼,想讓 hashset 支援自定義物件去重,只需要在自定義物件中重寫 hashcode 和 equals 方法即可,具體實現**如下:

@setter

@getter

@tostring

class person

@override

public boolean equals(object o)

@override

public int hashcode()

}重新執行以上**,執行結果如下圖所示:

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

我們只要了解了 hashset 執行新增元素的流程,就能知道為什麼 hashset 能保證元素不重複了?

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

為了更清楚的了解 hashset 的新增流程,我們可以嘗試閱讀 hashset 的具體實現原始碼,hashset 新增方法的實現原始碼如下(以下原始碼基於 jdk 8):

// hashmap 中 put() 返回 null 時,表示操作成功

public boolean add(e e)

從上述原始碼可以看出 hashset 中的 add 方法,實際呼叫的是 hashmap 中的 put,那麼我們繼續看 hashmap 中的 put 實現:

// 返回值:如果插入位置沒有元素則返回 null,否則返回上乙個元素

public v put(k key, v value)

從上述原始碼可以看出,hashmap 中的 put() 方法又呼叫了 putval() 方法,putval() 的原始碼如下:

final v putval(int hash, k key, v value, boolean onlyifabsent,

boolean evict)

// 鍊錶節點的與 put 操作

// 相同時,不做重複操作,跳出迴圈

if (e.hash == hash &&

((k = e.key) == key || (key != null && key.equals(k))))

break;

p = e;}}

// 找到或新建乙個 key 和 hashcode 與插入元素相等的鍵值對,進行 put 操作

if (e != null)

}// 更新結構化修改資訊

++modcount;

www.cppcns.com // 鍵值對數目超過閾值時,進行 rehash

if (++size > threshold)

resize();

// 插入後**

afternodeinsertion(evict);

return null;

}從上述原始碼可以看出,當將乙個鍵值對放入 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 判斷是否已存在,如果存在就不在重複插入了,這樣就保證了元素的不重複。

HashSet 如何保證元素不重複 hash碼

hashset 不重複主要add 方法實現,使用 add 方法找到是否存在元素,存在就不新增,不存在就新增。hashset 主要是基於hashmap 實現的,hashmap 的key就是 hashset 的元素,hashset 基於hash 函式實現元素不重複。首先看 add 方法 public b...

Set集合是如何保證元素不重複

在使用集合的時候,會經常用到set集合,set集合的特點如下 1,元素無序 2,元素不可重複 那麼set集合底層是如何保證元素無序的呢?1,在往set集合中新增物件的時候,首先會通過該物件的hashcode方法計算該物件的hash值。2,將計算出來的hash值去hash表中查詢,如果hash表中不存...

hashSet保證不重複的原理

總的來說,在向hashset中add 元素時,判斷元素是否存在的依據,不僅僅是hash碼值就能夠確定的,同時還要結合equles方法。hashset 類中的add 方法 public boolean add e e put 方法 public v put k key,v value modcount...