HashSet實(shí)現(xiàn)原理分析(Java源碼剖析)

本文將深入討論HashSet實(shí)現(xiàn)原理的源碼細(xì)節(jié)帆离。在分析源碼之前玩徊,首先我們需要對HashSet有一個基本的理解晰筛。

  • HashSet只存儲不同的值,set中是不會出現(xiàn)重復(fù)值的莹汤。

  • HashSet和HashMap一樣也需要實(shí)現(xiàn)hash算法來計算對象的hash值快鱼,但不同的是,HashMap中添加一個鍵值對的時候纲岭, (Key, Value)抹竹,hash函數(shù)計算的是Key的hash值。而HashSet則是計算value的hash值止潮。當(dāng)我們調(diào)用HashSet的add(E e)的方法 的時候窃判,我們會計算機(jī)元素e的hash值,如果這個值之前沒出現(xiàn)過沽翔,就說明這個元素在set中不存在兢孝,如果出現(xiàn)過,就說明仅偎。set中已經(jīng)存在了跨蟹,就添加失敗。

知道了上述的基本概念之后橘沥,我們就可以打開JDK源碼窗轩,來一探究竟了。

關(guān)于hashSet的實(shí)現(xiàn)原理座咆,最重要的一個點(diǎn)就是HashSet內(nèi)部是使用HashMap來存儲對象的痢艺。所以請讀者務(wù)必先對hashMap的實(shí)現(xiàn)原理有一個初步的認(rèn)識。參考筆者的文章HashMap實(shí)現(xiàn)原理分析(Java源碼剖析)

我們可以看到HashSet有多個構(gòu)造函數(shù)介陶,但每個構(gòu)造函數(shù)都是初始化了一個HashMap的對象

/**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

    /**
     * Constructs a new set containing the elements in the specified
     * collection.  The <tt>HashMap</tt> is created with default load factor
     * (0.75) and an initial capacity sufficient to contain the elements in
     * the specified collection.
     *
     * @param c the collection whose elements are to be placed into this set
     * @throws NullPointerException if the specified collection is null
     */
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * the specified initial capacity and the specified load factor.
     *
     * @param      initialCapacity   the initial capacity of the hash map
     * @param      loadFactor        the load factor of the hash map
     * @throws     IllegalArgumentException if the initial capacity is less
     *             than zero, or if the load factor is nonpositive
     */
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * the specified initial capacity and default load factor (0.75).
     *
     * @param      initialCapacity   the initial capacity of the hash table
     * @throws     IllegalArgumentException if the initial capacity is less
     *             than zero
     */
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

我們可以觀察到堤舒,默認(rèn)的構(gòu)造函數(shù)指定的初始化容量是16,負(fù)載因子是0.75.哺呜。也就是說創(chuàng)建了一個長度為16的數(shù)組舌缤,默認(rèn)的負(fù)載因子為0.75,當(dāng)達(dá)到容量時,map會自動擴(kuò)容国撵。

而這里的HashMap的是如下:

private transient HashMap<E,Object> map;

可以看到陵吸,HashSet中使用的HashMap,key為Set的元素類型介牙,value為Object壮虫。

add(E e)

我們來看add方法的實(shí)現(xiàn)

/**
     * Adds the specified element to this set if it is not already present.
     * More formally, adds the specified element <tt>e</tt> to this set if
     * this set contains no element <tt>e2</tt> such that
     * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
     * If this set already contains the element, the call leaves the set
     * unchanged and returns <tt>false</tt>.
     *
     * @param e element to be added to this set
     * @return <tt>true</tt> if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

PRESENT的定義

// Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

我們看到PRESENT是一個靜態(tài)的類對象,Object類型环础。所有HashSet的實(shí)例都共享這個對象囚似。
也就是說,我們在向set中添加一個e元素的時候喳整,實(shí)際上就是在像map添加一個(e, Object)的鍵值對谆构。我們添加的元素e變成了map中的key,而value則都是Obeject對象框都。又因?yàn)閙ap中key值是唯一的,而value是可以重復(fù)的呵晨。所以我們添加的e作為key的話魏保,就可以保證添加成功的話,e一定是唯一的摸屠。這就實(shí)現(xiàn)了set的唯一性谓罗。

remove(Object o)

我們接下來看remove的代碼

/**
     * Removes the specified element from this set if it is present.
     * More formally, removes an element <tt>e</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>,
     * if this set contains such an element.  Returns <tt>true</tt> if
     * this set contained the element (or equivalently, if this set
     * changed as a result of the call).  (This set will not contain the
     * element once the call returns.)
     *
     * @param o object to be removed from this set, if present
     * @return <tt>true</tt> if the set contained the specified element
     */
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

我們知道Hashmap中移除一個key的話,會返回這個key值鎖對應(yīng)的value季二,而我們這里的map檩咱,所有的key的value都是同一個對象PRESENT,所以我們這里只需要判斷map.remove(o)的返回值是不是PRESENT胯舷,就可以確定是否成功移除了

iterator()

我們知道hashSet沒有g(shù)et方法刻蚯,想要獲取HashSet的元素需要調(diào)用iterator()
為什么會這樣呢?
其實(shí)只要我們結(jié)合HashSet底層是由HashMap實(shí)現(xiàn)的就知道桑嘶,我們添加的元素值都被map當(dāng)成了key來存儲炊汹,顯然沒有從map中獲取單獨(dú)一個key的方法,但是我們可以獲取所有key逃顶,調(diào)用keySet方法即可讨便。

/**
     * Returns an iterator over the elements in this set.  The elements
     * are returned in no particular order.
     *
     * @return an Iterator over the elements in this set
     * @see ConcurrentModificationException
     */
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

小結(jié)

HashSet由于內(nèi)部實(shí)現(xiàn)是完全基于HashMap的,所以原理較為簡單以政,代碼也不多霸褒,源碼加上注釋也就是300多行。

關(guān)于hashSet的實(shí)現(xiàn)原理盈蛮,我們需要掌握一下幾點(diǎn)

  • hashSet內(nèi)部用HashMap來存儲元素

  • HashSet利用本身的值來計算hash值废菱,因?yàn)橹当划?dāng)作hashmap的key,而hashmap是利用key來計算hash值的

  • 因?yàn)閔ashset將value當(dāng)作key來存儲,所以根據(jù)map的key值唯一的原理昙啄,我們就可以實(shí)現(xiàn)set的無重復(fù)元素的功能

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末穆役,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子梳凛,更是在濱河造成了極大的恐慌耿币,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件韧拒,死亡現(xiàn)場離奇詭異淹接,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)叛溢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門塑悼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人楷掉,你說我怎么就攤上這事厢蒜。” “怎么了烹植?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵斑鸦,是天一觀的道長。 經(jīng)常有香客問我草雕,道長巷屿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任墩虹,我火速辦了婚禮嘱巾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诫钓。我一直安慰自己旬昭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布尖坤。 她就那樣靜靜地躺著稳懒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪慢味。 梳的紋絲不亂的頭發(fā)上场梆,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機(jī)與錄音纯路,去河邊找鬼或油。 笑死,一個胖子當(dāng)著我的面吹牛驰唬,可吹牛的內(nèi)容都是我干的顶岸。 我是一名探鬼主播腔彰,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼辖佣!你這毒婦竟也來了霹抛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤卷谈,失蹤者是張志新(化名)和其女友劉穎杯拐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體世蔗,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡端逼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了污淋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顶滩。...
    茶點(diǎn)故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖寸爆,靈堂內(nèi)的尸體忽然破棺而出礁鲁,到底是詐尸還是另有隱情,我是刑警寧澤而昨,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布救氯,位于F島的核電站,受9級特大地震影響歌憨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜墩衙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一务嫡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧漆改,春花似錦心铃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至樊破,卻和暖如春愉棱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背哲戚。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工奔滑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人顺少。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓朋其,卻偏偏與公主長得像王浴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子梅猿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容