HashMap整理

歡迎訪問我的博客:http://wangnan.tech

參考:

HashMap數(shù)據(jù)結(jié)構(gòu)

數(shù)組

數(shù)組存儲區(qū)間是連續(xù)的,占用內(nèi)存嚴(yán)重,故空間復(fù)雜的很大驰弄。但數(shù)組的二分查找時間復(fù)雜度小,為O(1)落追;數(shù)組的特點(diǎn)是:尋址容易,插入和刪除困難涯肩;

鏈表

鏈表存儲區(qū)間離散轿钠,占用內(nèi)存比較寬松,故空間復(fù)雜度很小病苗,但時間復(fù)雜度很大疗垛,達(dá)O(N)。鏈表的特點(diǎn)是:尋址困難硫朦,插入和刪除容易贷腕。

哈希表

首先HashMap里面實現(xiàn)一個靜態(tài)內(nèi)部類Entry,其重要的屬性有 key , value, next,從屬性key,value我們就能很明顯的看出來Entry就是HashMap鍵值對實現(xiàn)的一個基礎(chǔ)bean泽裳,我們上面說到HashMap的基礎(chǔ)就是一個線性數(shù)組瞒斩,這個數(shù)組就是Entry[],Map里面的內(nèi)容都保存在Entry[]里面涮总。

    /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */
    transient Entry[] table;

那么我們能不能綜合兩者的特性胸囱,做出一種尋址容易,插入刪除也容易的數(shù)據(jù)結(jié)構(gòu)瀑梗?答案是肯定的烹笔,這就是我們要提起的哈希表。哈希表((Hash table)既滿足了數(shù)據(jù)的查找方便抛丽,同時不占用太多的內(nèi)容空間箕宙,使用也十分方便。

哈希表是由數(shù)組+鏈表組成的铺纽,一個長度為16的數(shù)組中,每個元素存儲的是一個鏈表的頭結(jié)點(diǎn)哟忍。那么這些元素是按照什么樣的規(guī)則存儲到數(shù)組中呢狡门。一般情況是通過hash(key)%len獲得,也就是元素的key的哈希值對數(shù)組長度取模得到

簡單地說锅很,HashMap 在底層將 key-value 當(dāng)成一個整體進(jìn)行處理其馏,這個整體就是一個 Entry 對象。HashMap 底層采用一個 Entry[] 數(shù)組來保存所有的 key-value 對爆安,當(dāng)需要存儲一個 Entry 對象時叛复,會根據(jù) hash 算法來決定其在數(shù)組中的存儲位置,在根據(jù) equals 方法決定其在該數(shù)組位置上的鏈表中的存儲位置扔仓;當(dāng)需要取出一個Entry 時褐奥,也會根據(jù) hash 算法找到其在數(shù)組中的存儲位置,再根據(jù) equals 方法從該位置上的鏈表中取出該Entry翘簇。

put

 public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value); //null總是放在數(shù)組的第一個鏈表中
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        //遍歷鏈表
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //如果key在鏈表中已存在撬码,則替換為新value
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }
    
    void addEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e); //參數(shù)e, 是Entry.next
    //如果size超過threshold,則擴(kuò)充table大小版保。再散列
    if (size++ >= threshold)
            resize(2 * table.length);
}

get

 public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        //先定位到數(shù)組元素呜笑,再遍歷該元素處的鏈表
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
}

初始大小

  public HashMap(int initialCapacity, float loadFactor) {
        .....         // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        table = new Entry[capacity];
        init();
    }

rehash

當(dāng) HashMap 中的元素越來越多的時候,hash沖突的幾率也就越來越高彻犁,因為數(shù)組的長度是固定的叫胁。所以為了提高查詢的效率,就要對 HashMap 的數(shù)組進(jìn)行擴(kuò)容汞幢,數(shù)組擴(kuò)容這個操作也會出現(xiàn)在 ArrayList 中驼鹅,這是一個常用的操作,而在 HashMap 數(shù)組擴(kuò)容之后,最消耗性能的點(diǎn)就出現(xiàn)了:原數(shù)組中的數(shù)據(jù)必須重新計算其在新數(shù)組中的位置谤民,并放進(jìn)去堰酿,這就是 resize。

那么 HashMap 什么時候進(jìn)行擴(kuò)容呢张足?當(dāng) HashMap 中的元素個數(shù)超過數(shù)組大小 loadFactor時触创,就會進(jìn)行數(shù)組擴(kuò)容,loadFactor的默認(rèn)值為 0.75为牍,這是一個折中的取值哼绑。也就是說,默認(rèn)情況下碉咆,數(shù)組大小為 16抖韩,那么當(dāng) HashMap 中元素個數(shù)超過 160.75=12 的時候,就把數(shù)組的大小擴(kuò)展為 2*16=32疫铜,即擴(kuò)大一倍茂浮,然后重新計算每個元素在數(shù)組中的位置,而這是一個非常消耗性能的操作壳咕,所以如果我們已經(jīng)預(yù)知 HashMap 中元素的個數(shù)席揽,那么預(yù)設(shè)元素的個數(shù)能夠有效的提高 HashMap 的性能。

Fail-Fast 機(jī)制

我們知道 java.util.HashMap 不是線程安全的谓厘,因此如果在使用迭代器的過程中有其他線程修改了 map幌羞,那么將拋出 ConcurrentModificationException,這就是所謂 fail-fast 策略竟稳。
ail-fast 機(jī)制是 java 集合(Collection)中的一種錯誤機(jī)制属桦。 當(dāng)多個線程對同一個集合的內(nèi)容進(jìn)行操作時,就可能會產(chǎn)生 fail-fast 事件他爸。
例如:當(dāng)某一個線程 A 通過 iterator去遍歷某集合的過程中聂宾,若該集合的內(nèi)容被其他線程所改變了;那么線程 A 訪問集合時诊笤,就會拋出 ConcurrentModificationException 異常亏吝,產(chǎn)生 fail-fast 事件。

由所有 HashMap 類的“collection 視圖方法”所返回的迭代器都是快速失敗的:在迭代器創(chuàng)建之后盏混,如果從結(jié)構(gòu)上對映射進(jìn)行修改蔚鸥,除非通過迭代器本身的 remove 方法,其他任何時間任何方式的修改许赃,迭代器都將拋出 ConcurrentModificationException

HashMap 的兩種遍歷方式

  Map map = new HashMap();
  Iterator iter = map.entrySet().iterator();
  while (iter.hasNext()) {
  Map.Entry entry = (Map.Entry) iter.next();
  Object key = entry.getKey();
  Object val = entry.getValue();
  }

效率高

  Map map = new HashMap();
  Iterator iter = map.keySet().iterator();
  while (iter.hasNext()) {
  Object key = iter.next();
  Object val = map.get(key);

效率低

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末止喷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子混聊,更是在濱河造成了極大的恐慌弹谁,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異预愤,居然都是意外死亡沟于,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門植康,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旷太,“玉大人,你說我怎么就攤上這事销睁」╄担” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵冻记,是天一觀的道長睡毒。 經(jīng)常有香客問我,道長冗栗,這世上最難降的妖魔是什么演顾? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮隅居,結(jié)果婚禮上钠至,老公的妹妹穿的比我還像新娘。我一直安慰自己军浆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布挡闰。 她就那樣靜靜地躺著,像睡著了一般摄悯。 火紅的嫁衣襯著肌膚如雪赞季。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天奢驯,我揣著相機(jī)與錄音义黎,去河邊找鬼廉涕。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的治力。 我是一名探鬼主播晕讲,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼马澈!你這毒婦竟也來了瓢省?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤痊班,失蹤者是張志新(化名)和其女友劉穎勤婚,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涤伐,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡馒胆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了凝果。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祝迂。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖器净,靈堂內(nèi)的尸體忽然破棺而出型雳,到底是詐尸還是另有隱情,我是刑警寧澤山害,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布纠俭,位于F島的核電站,受9級特大地震影響浪慌,放射性物質(zhì)發(fā)生泄漏柑晒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一眷射、第九天 我趴在偏房一處隱蔽的房頂上張望匙赞。 院中可真熱鬧佛掖,春花似錦、人聲如沸涌庭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坐榆。三九已至拴魄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間席镀,已是汗流浹背匹中。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留豪诲,地道東北人顶捷。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像屎篱,于是被迫代替她去往敵國和親服赎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

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

  • 一交播、hashmap 數(shù)據(jù)結(jié)構(gòu) HashMap 主要的操作一般包括: V put(K key, V value) V...
    huashu閱讀 245評論 0 2
  • 實際上重虑,HashSet 和 HashMap 之間有很多相似之處,對于 HashSet 而言秦士,系統(tǒng)采用 Hash 算...
    曹振華閱讀 2,513評論 1 37
  • HashMap 是 Java 面試必考的知識點(diǎn)缺厉,面試官從這個小知識點(diǎn)就可以了解我們對 Java 基礎(chǔ)的掌握程度。網(wǎng)...
    野狗子嗷嗷嗷閱讀 6,667評論 9 107
  • 放鶴亭前博愛深 情人梅下看花遲 悵然淡問吳王事 猶得夢痕空嘆枝
    摩羯星一號閱讀 232評論 0 4
  • 有你在关贵, 我以為我的世界 不會再出現(xiàn)霧霾 但遇骑, 一轉(zhuǎn)念 你在自己的小世界里 忙碌著 一邊拿著手機(jī) 說卖毁,親愛的, 自...
    東方清羽閱讀 262評論 0 4