目錄
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...