HashSet源碼解析從一道面試題說起:HashSet內(nèi)部是怎么實現(xiàn)的?

本文原創(chuàng)地址,我的博客https://jsbintask.cn/2019/03/27/jdk/jdk8-hashset/(食用效果最佳),轉(zhuǎn)載請注明出處!

前言

前段時間朋友面試遇到這個問題:談一談HashSet的特點,它是怎么實現(xiàn)的,使用時有什么需要注意的點呢?恰好最近在寫這方面的文章,于是正好通過本篇文章講解下HashSet的源碼實現(xiàn),需要注意的點。
HashSet實現(xiàn)了Set接口,是一個不能夠存放重復元素的容器,內(nèi)部直接使用HashMap實現(xiàn),即底層使用數(shù)組存儲數(shù)據(jù),HashSet沒有任何同步手段,在多線程環(huán)境下需慎重考慮,可以使用Collections.synchronizedSet(new HashSet(...));給原有的Set方法同步。

HashSet

友情鏈接:HashMap源碼全解析從一道面試題說起:請一行一行代碼描述下hashmap put方法

HashSet結(jié)構(gòu)詳解

類結(jié)構(gòu)

HashSet

關(guān)系圖沒有需要注意的點,HashSet實現(xiàn)了Set接口,Set是集合的抽象概念,它內(nèi)部不允許出現(xiàn)重復的元素。

類成員

前面我們已經(jīng)說過,Set內(nèi)部是不能夠存在重復元素的,那HashSet內(nèi)部是怎么做的呢?如圖:


HashSet

直接使用HashMap存放數(shù)據(jù),因HashMap的Key須唯一,所以可以將我們需要存放的數(shù)據(jù)放到Key,而所有的value對應一個內(nèi)部的對象PRESENT即可。

源碼詳解

構(gòu)造器

public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}

簡單明了,直接new一個HashMap。 這里需要注意的是,它還有另一個不常用的構(gòu)造方法:

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

使用這個構(gòu)造方法會內(nèi)部將不再使用HashMap操作元素,而是LinkedHashMap,而LinkedHashMap繼承自HashMap,他們之間的不同是LinkedHashMap在HashMap底層使用數(shù)組的是線上加了兩個“指針”分別指向頭和尾。

HashSet

add(E e) 添加元素

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

直接調(diào)用HashMap的put方法,將元素e放到map的key位置來保證唯一性。我們知道HashMap的put方法如果該位置已經(jīng)存在一個一樣的Key(==或者equals相等),會用新的value替換原來的舊的value,并且返回舊的value,所以對于HashSet而言第一次插入返回null就代表成功,以后再次插入同樣的元素,返回的是一個對象,表示已經(jīng)存在這樣的元素了,插入失?。?br> 其它remove,contains方法類似。

注意點

  1. HashSet中存儲的元素都是“無序的”,因為底層使用數(shù)組實現(xiàn),存儲時將key進行hash得出數(shù)組位置,這是一個隨機的過程,所以存儲的元素時無序的。
  2. 為了HashSet迭代時的性能考慮,初始容量可以盡量設(shè)置的小一點,而加載引子則可以設(shè)置的大一點(默認0.75)。因為HashSet和HashMap關(guān)注的中心不同,HashMap關(guān)注的是其中的鍵值對的存儲以及擴容時的性能考慮。
    HashSet的迭代方法直接調(diào)用HashMap內(nèi)部KeySet的iterator方法,返回KeyIterator。
public Iterator<E> iterator() {
    return map.keySet().iterator();
}
HashSet

該方法在迭代時循環(huán)判斷數(shù)組是否為null,不為null則認為該位置上有元素。所以確定初始容量,盡量設(shè)置的更小有利于HashSet迭代其中的key。

總結(jié)

  1. HashSet是一個元素不會重復并且無序的容器。
  2. HashSet內(nèi)部使用HashMap實現(xiàn),即最終依然使用數(shù)組存儲數(shù)據(jù)。
  3. 使用時應該盡可能確定容器的大小,盡量設(shè)置初始容量小一點,并且加載引子大一點以加快迭代性能。

關(guān)注我,這里只有干貨!

關(guān)聯(lián)文章:
HashMap源碼全解析從一道面試題說起:請一行一行代碼描述下hashmap put方法
jdk1.8源碼解析-ArrayList
jdk1.8 LinkedList源碼全分析
線程池?面試?看這篇就夠了!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 實際上,HashSet 和 HashMap 之間有很多相似之處,對于 HashSet 而言,系統(tǒng)采用 Hash 算...
    曹振華閱讀 2,564評論 1 37
  • Java集合類可用于存儲數(shù)量不等的對象,并可以實現(xiàn)常用的數(shù)據(jù)結(jié)構(gòu)如棧,隊列等,Java集合還可以用于保存具有映射關(guān)...
    小徐andorid閱讀 2,101評論 0 13
  • 搞懂 HashSet & LinkedHashSet 源碼 以及集合常見面試題目 經(jīng)過上兩篇的 HashMap 和...
    醒著的碼者閱讀 2,606評論 1 5
  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,917評論 0 11
  • 我有夢 后來,它成了夢 再后來,夢也夢不見了
    詩無邪閱讀 148評論 1 1

友情鏈接更多精彩內(nèi)容