HashMap源碼之一:概述

????HashMap的重要性就不多說了,經(jīng)常用到渤弛,面試也很喜歡問,所以有必要研究下。

????先來看下HashMap的結(jié)構(gòu)籽前,HashMap的結(jié)構(gòu)的核心組成是一個數(shù)組和一個鏈表/紅黑樹,如下所示:
HashMap結(jié)構(gòu)1.png

????當(dāng)調(diào)用put方法往HashMap存入數(shù)據(jù)時,HashMap的數(shù)據(jù)分布可能如下,需要注意的是齐婴,數(shù)組里面裝的元素是一個節(jié)點,Node類型或者TreeNode型;這種類型包裝了我們傳進(jìn)來的鍵值對key ---> value:
HashMap結(jié)構(gòu)2.png

????可以看到,這些數(shù)據(jù)不一定是連續(xù)的匀哄;那么這些數(shù)據(jù)是怎么確定放在數(shù)組的哪個桶的呢?這就要借助于hash算法了法梯,當(dāng)一個數(shù)據(jù)要存入HashMap時,拿鍵的hash值與數(shù)組長度 - 1做與運算,即:

(n - 1) & hash

????其中n是數(shù)組長度捂掰,hash是key的hash鸥昏。
????當(dāng)存入的數(shù)據(jù)很多時敛腌,每個key之間的hash值可能就一樣了像樊,不同的key也可能產(chǎn)生不同的hash值;如果hash值一樣的話涂滴,那么根據(jù)上面的公式柔纵,桶的位置也就一樣了;一個桶裝多個數(shù)據(jù)或详,怎么辦霸琴?

????多個key的hash一樣的現(xiàn)象叫做hash碰撞或者h(yuǎn)ash沖突昭伸,HashMap是這樣處理這種碰撞的庐杨,如果兩個節(jié)點發(fā)生了碰撞,那么把兩個節(jié)點連在一起学歧,形成一個鏈表:
HashMap結(jié)構(gòu)3.png

????如上圖所示枝笨,如果E的hash和X的hash值相等,那么就發(fā)生了hash沖突,這時候把X連在E的后面徙融;對于一個HashMap來說洒缀,碰撞發(fā)生的越少树绩,查找效率就越高饺饭;因為碰撞太多的話瘫俊,這個鏈表就越長(或者紅黑樹越高)悴灵,這樣在get數(shù)據(jù)的時候就需要遍歷鏈表或者紅黑樹,這樣就降低了效率胸哥;總之空厌,數(shù)據(jù)分布越均勻银酬,查找效率越高揩瞪。
????當(dāng)鏈表的長度過長時李破,HashMap就會把鏈表轉(zhuǎn)換成紅黑樹:
HashMap結(jié)構(gòu)4.png

????鏈表轉(zhuǎn)換成紅黑樹的過程叫做樹化(下篇文章會分析);當(dāng)鏈表的節(jié)點快速增加時毛嫉,會造成搜索效率的降低承粤,而紅黑樹能夠解決搜索效率問題,所以樹化就出現(xiàn)了仙粱。

????廢話不多說彻舰,直接上源碼刃唤,先看類信息和構(gòu)造函數(shù):

//繼承自AbstractMap透揣,實現(xiàn)了Map辐真、Cloneable和Serializable接口
//AbstractMap是一個實現(xiàn)了Map接口的抽象類,他對Map接口進(jìn)行了有限
//的擴(kuò)展侍咱,同時實現(xiàn)了部分接口方法楔脯,比如size()胯甩、clear()偎箫、hashCode()等
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

    /**
     * The default initial capacity - MUST be a power of two.
     */
    //HashMap的默認(rèn)初始化容量16淹办,這個容量比如是2的n次冪
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    /**
     * The maximum capacity, used if a higher value is implicitly specified
     * by either of the constructors with arguments.
     * MUST be a power of two <= 1<<30.
     */
    //HashMap的最大容量,2的30次冪
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * The load factor used when none specified in constructor.
     */
    //HashMap的默認(rèn)加載因子速挑,加載因子的作用是判斷是不是需要擴(kuò)容了
    //比如初始容量是16姥宝,加載因子是0.75,16 * 0.75 = 12伶授,也就是說
    //當(dāng)HashMap里面的元素個數(shù)達(dá)到12個的時候就需要對HashMap進(jìn)行擴(kuò)容了
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * The bin count threshold for using a tree rather than list for a
     * bin.  Bins are converted to trees when adding an element to a
     * bin with at least this many nodes. The value must be greater
     * than 2 and should be at least 8 to mesh with assumptions in
     * tree removal about conversion back to plain bins upon
     * shrinkage.
     */
    //當(dāng)鏈表中的節(jié)點達(dá)到8個時违诗,就將鏈表轉(zhuǎn)換成紅黑樹
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * The bin count threshold for untreeifying a (split) bin during a
     * resize operation. Should be less than TREEIFY_THRESHOLD, and at
     * most 6 to mesh with shrinkage detection under removal.
     */
    //當(dāng)紅黑樹的節(jié)點數(shù)量小于等于6時疮蹦,將紅黑樹轉(zhuǎn)換成鏈表
    static final int UNTREEIFY_THRESHOLD = 6;

    /**
     * The smallest table capacity for which bins may be treeified.
     * (Otherwise the table is resized if too many nodes in a bin.)
     * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
     * between resizing and treeification thresholds.
     */
    //鏈表轉(zhuǎn)換成樹的必要條件之一愕乎,除了上面的閾值判斷之外,還會判斷HashMap的容量
    //只有容量大于64的時候绅项,才會轉(zhuǎn)換比肄;小于這個值時芳绩,HashMap寧愿選擇擴(kuò)容妥色,而不是
    //樹化建立初期,多個鍵值對放入同一個鏈表中而導(dǎo)致不必要的轉(zhuǎn)換
    static final int MIN_TREEIFY_CAPACITY = 64;

    /**
     * The table, initialized on first use, and resized as
     * necessary. When allocated, length is always a power of two.
     * (We also tolerate length zero in some operations to allow
     * bootstrapping mechanics that are currently not needed.)
     */
    //注釋比較簡單撮竿,自己看幢踏。HashMap里面的數(shù)據(jù)首先是存放在數(shù)組里面
    //的長度不夠的時候惑折,我們可以擴(kuò)容惨驶,這個容量都是2的n次冪粗卜,transient
    //代表該變量無法被序列化(HashMap本身是實現(xiàn)了序列化接口的)
    transient Node<K,V>[] table;
    
    /**
     * The number of key-value mappings contained in this map.
     */
    //size代表該Map里面鍵值對的個數(shù)纳击,其實就是HashMap的元素個數(shù)
    transient int size;

    /**
     * The next size value at which to resize (capacity * load factor).
     *
     * @serial
     */
    // (The javadoc description is true upon serialization.
    // Additionally, if the table array has not been allocated, this
    // field holds the initial array capacity, or zero signifying
    // DEFAULT_INITIAL_CAPACITY.)
    //擴(kuò)容閾值,當(dāng)HashMap里面的元素個數(shù)達(dá)到這個值的時候就必須
    //擴(kuò)容了刨啸,這個值是由容量*加載因子確定的,默認(rèn)的加載因子是0.75
    int threshold;

    /**
     * The load factor for the hash table.
     *
     * @serial
     */
    //加載因子
    final float loadFactor;

    /**
     * The number of times this HashMap has been structurally modified
     * Structural modifications are those that change the number of mappings in
     * the HashMap or otherwise modify its internal structure (e.g.,
     * rehash).  This field is used to make iterators on Collection-views of
     * the HashMap fail-fast.  (See ConcurrentModificationException).
     */
    transient int modCount;


    /**
     * Constructs an empty <tt>HashMap</tt> with the specified initial
     * capacity and load factor.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    //構(gòu)造函數(shù),開發(fā)中直接調(diào)用這個構(gòu)造函數(shù)的比較少离例,這個構(gòu)造函數(shù)指定了默認(rèn)
    //容量和加載因子
    public HashMap(int initialCapacity, float loadFactor) {
        //安全判斷悉稠,簡單
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }

    /**
     * Constructs an empty <tt>HashMap</tt> with the specified initial
     * capacity and the default load factor (0.75).
     *
     * @param  initialCapacity the initial capacity.
     * @throws IllegalArgumentException if the initial capacity is negative.
     */
    //只指定默認(rèn)容量的構(gòu)造函數(shù)耀盗,加載因子還是用默認(rèn)的0.75
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }


    /**
     * Constructs an empty <tt>HashMap</tt> with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    //這種構(gòu)造函數(shù)用的最多袍冷,什么都是默認(rèn)的
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

    /**
     * Constructs a new <tt>HashMap</tt> with the same mappings as the
     * specified <tt>Map</tt>.  The <tt>HashMap</tt> is created with
     * default load factor (0.75) and an initial capacity sufficient to
     * hold the mappings in the specified <tt>Map</tt>.
     *
     * @param   m the map whose mappings are to be placed in this map
     * @throws  NullPointerException if the specified map is null
     */
    //根據(jù)一個Map構(gòu)造一個HashMap
    public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末猫牡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子邓线,更是在濱河造成了極大的恐慌淌友,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件骇陈,死亡現(xiàn)場離奇詭異震庭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)你雌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門器联,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人婿崭,你說我怎么就攤上這事拨拓。” “怎么了氓栈?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長丘侠。 經(jīng)常有香客問我,道長秽澳,這世上最難降的妖魔是什么始花? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任浇垦,我火速辦了婚禮朴摊,結(jié)果婚禮上朦前,老公的妹妹穿的比我還像新娘。我一直安慰自己悲靴,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布胳徽。 她就那樣靜靜地躺著,像睡著了一般箫爷。 火紅的嫁衣襯著肌膚如雪衩婚。 梳的紋絲不亂的頭發(fā)上柱徙,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死腹尖,一個胖子當(dāng)著我的面吹牛绎巨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼撼港,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了开瞭?” 一聲冷哼從身側(cè)響起葱色,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤烘绽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體额获,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年憎兽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡嚼摩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情躏精,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布筏餐,位于F島的核電站魁瞪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一妨马、第九天 我趴在偏房一處隱蔽的房頂上張望脖咐。 院中可真熱鬧硝皂,春花似錦、人聲如沸稽物。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贝或。三九已至吼过,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咪奖,已是汗流浹背盗忱。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留羊赵,地道東北人趟佃。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像昧捷,于是被迫代替她去往敵國和親闲昭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

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