Hashmap

定義

Hashmap是map接口的常用實(shí)現(xiàn)類患膛。
Hashmap中put方法的源碼如下:

    public V put(K key, V value)   
    {   
     // 如果 key 為 null,調(diào)用 putForNullKey 方法進(jìn)行處理  
     if (key == null)   
         return putForNullKey(value);   
     // 根據(jù) key 的 keyCode 計(jì)算 Hash 值  
     int hash = hash(key.hashCode());   
     // 搜索指定 hash 值在對(duì)應(yīng) table 中的索引  
         int i = indexFor(hash, table.length);  
     // 如果 i 索引處的 Entry 不為 null,通過(guò)循環(huán)不斷遍歷 e 元素的下一個(gè)元素  
     for (Entry<K,V> e = table[i]; e != null; e = e.next)   
     {   
         Object k;   
         // 找到指定 key 與需要放入的 key 相等(hash 值相同  
         // 通過(guò) equals 比較放回 true)  
         if (e.hash == hash && ((k = e.key) == key   
             || key.equals(k)))   
         {   
             V oldValue = e.value;   
             e.value = value;   
             e.recordAccess(this);   
             return oldValue;   
         }   
     }   
     // 如果 i 索引處的 Entry 為 null戒祠,表明此處還沒(méi)有 Entry   
     modCount++;   
     // 將 key弥臼、value 添加到 i 索引處  
     addEntry(hash, key, value, i);   
     return null;   
    }   

上面程序中用到了一個(gè)重要的內(nèi)部接口:Map.Entry,每個(gè) Map.Entry 其實(shí)就是一個(gè) key-value 對(duì)朝群。從上面程序中可以看出:當(dāng)系統(tǒng)決定存儲(chǔ) HashMap 中的 key-value 對(duì)時(shí)燕耿,完全沒(méi)有考慮 Entry 中的 value,僅僅只是根據(jù) key 來(lái)計(jì)算并決定每個(gè) Entry 的存儲(chǔ)位置姜胖。這也說(shuō)明了前面的結(jié)論:我們完全可以把 Map 集合中的 value 當(dāng)成 key 的附屬誉帅,當(dāng)系統(tǒng)決定了 key 的存儲(chǔ)位置之后,value 隨之保存在那里即可。

所以蚜锨,若Hashmap在插入key時(shí)档插,
HashMap會(huì)先用key的hash值來(lái)檢查是否發(fā)生了hash碰撞,也就是對(duì)應(yīng)的位置是否為空踏志。如果原本已經(jīng)存在對(duì)應(yīng)的key阀捅,則直接改變對(duì)應(yīng)的value,并返回舊的value针余,而在判斷key是否存在的時(shí)候是先比較key的hashCode饲鄙,再比較相等或equals的。

Hashmap的構(gòu)造

HashMap 包含如下幾個(gè)構(gòu)造器:
* HashMap():構(gòu)建一個(gè)初始容量為 16圆雁,負(fù)載因子為 0.75 的 HashMap忍级。
* HashMap(int initialCapacity):構(gòu)建一個(gè)初始容量為 initialCapacity,負(fù)載因子為 0.75 的 HashMap伪朽。
* HashMap(int initialCapacity, float loadFactor):以指定初始容量轴咱、指定的負(fù)載因子創(chuàng)建一個(gè) HashMap。

對(duì)于 HashMap 及其子類而言烈涮,它們采用 Hash 算法來(lái)決定集合中元素的存儲(chǔ)位置朴肺。當(dāng)系統(tǒng)開(kāi)始初始化 HashMap 時(shí),系統(tǒng)會(huì)創(chuàng)建一個(gè)長(zhǎng)度為 capacity 的 Entry 數(shù)組坚洽,這個(gè)數(shù)組里可以存儲(chǔ)元素的位置被稱為“桶(bucket)”戈稿,每個(gè) bucket 都有其指定索引,系統(tǒng)可以根據(jù)其索引快速訪問(wèn)該 bucket 里存儲(chǔ)的元素讶舰。

無(wú)論何時(shí)鞍盗,HashMap 的每個(gè)“桶”只存儲(chǔ)一個(gè)元素(也就是一個(gè) Entry),由于 Entry 對(duì)象可以包含一個(gè)引用變量(就是 Entry 構(gòu)造器的的最后一個(gè)參數(shù))用于指向下一個(gè) Entry跳昼,因此可能出現(xiàn)的情況是:HashMap 的 bucket 中只有一個(gè) Entry般甲,但這個(gè) Entry 指向另一個(gè) Entry ——這就形成了一個(gè) Entry 鏈。如圖 1 所示:


Hashmap的讀取

當(dāng) HashMap 的每個(gè) bucket 里存儲(chǔ)的 Entry 只是單個(gè) Entry ——也就是沒(méi)有通過(guò)指針產(chǎn)生 Entry 鏈時(shí)鹅颊,此時(shí)的 HashMap 具有最好的性能:當(dāng)程序通過(guò) key 取出對(duì)應(yīng) value 時(shí)敷存,系統(tǒng)只要先計(jì)算出該 key 的 hashCode() 返回值,在根據(jù)該 hashCode 返回值找出該 key 在 table 數(shù)組中的索引堪伍,然后取出該索引處的 Entry历帚,最后返回該 key 對(duì)應(yīng)的 value 即可「苡椋看 HashMap 類的 get(K key) 方法代碼:

    public V get(Object key)   
    {   
     // 如果 key 是 null,調(diào)用 getForNullKey 取出對(duì)應(yīng)的 value   
     if (key == null)   
         return getForNullKey();   
     // 根據(jù)該 key 的 hashCode 值計(jì)算它的 hash 碼  
     int hash = hash(key.hashCode());   
     // 直接取出 table 數(shù)組中指定索引處的值谱煤,  
     for (Entry<K,V> e = table[indexFor(hash, table.length)];   
         e != null;   
         // 搜索該 Entry 鏈的下一個(gè) Entr   
         e = e.next)         // ①  
     {   
         Object k;   
         // 如果該 Entry 的 key 與被搜索 key 相同  
         if (e.hash == hash && ((k = e.key) == key   
             || key.equals(k)))   
             return e.value;   
     }   
     return null;   
    }   

從上面代碼中可以看出摊求,如果 HashMap 的每個(gè) bucket 里只有一個(gè) Entry 時(shí),HashMap 可以根據(jù)索引刘离、快速地取出該 bucket 里的 Entry室叉;在發(fā)生“Hash 沖突”的情況下睹栖,單個(gè) bucket 里存儲(chǔ)的不是一個(gè) Entry,而是一個(gè) Entry 鏈茧痕,系統(tǒng)只能必須按順序遍歷每個(gè) Entry野来,直到找到想搜索的 Entry 為止——如果恰好要搜索的 Entry 位于該 Entry 鏈的最末端(該 Entry 是最早放入該 bucket 中),那系統(tǒng)必須循環(huán)到最后才能找到該元素踪旷。
由此曼氛,我們可以在創(chuàng)建 HashMap 時(shí)根據(jù)實(shí)際需要適當(dāng)?shù)卣{(diào)整 load factor 的值;如果程序比較關(guān)心空間開(kāi)銷令野、內(nèi)存比較緊張舀患,可以適當(dāng)?shù)卦黾迂?fù)載因子;如果程序比較關(guān)心時(shí)間開(kāi)銷气破,內(nèi)存比較寬裕則可以適當(dāng)?shù)臏p少負(fù)載因子聊浅。

Hashmap的遍歷

public class HashMapDemo {  
  
    public static void main(String[] args) {  
          
        HashMap<String, String> hashMap = new HashMap<String, String>();  
        hashMap.put("cn", "中國(guó)");  
        hashMap.put("jp", "日本");  
        hashMap.put("fr", "法國(guó)");  
          
        System.out.println(hashMap);  
        System.out.println("cn:" + hashMap.get("cn"));  
        System.out.println(hashMap.containsKey("cn"));  
        System.out.println(hashMap.keySet());  
        System.out.println(hashMap.isEmpty());  
          
        hashMap.remove("cn");  
        System.out.println(hashMap.containsKey("cn"));  
          
        //采用Iterator遍歷HashMap  
        Iterator it = hashMap.keySet().iterator();  
        while(it.hasNext()) {  
            String key = (String)it.next();  
            System.out.println("key:" + key);  
            System.out.println("value:" + hashMap.get(key));  
        }  
          
        //遍歷HashMap的另一個(gè)方法  
        Set<Entry<String, String>> sets = hashMap.entrySet();  
        for(Entry<String, String> entry : sets) {  
            System.out.print(entry.getKey() + ", ");  
            System.out.println(entry.getValue());  
        }  
    }  
}  

Maphash中的一些方法

Maphash.keySet獲得map的鍵集合

Maphash中的紅黑樹(shù)(待)

JDK 1.8 以前 HashMap 的實(shí)現(xiàn)是 數(shù)組+鏈表,即使哈希函數(shù)取得再好现使,也很難達(dá)到元素百分百均勻分布低匙。

當(dāng) HashMap 中有大量的元素都存放到同一個(gè)桶中時(shí),這個(gè)桶下有一條長(zhǎng)長(zhǎng)的鏈表碳锈,這個(gè)時(shí)候 HashMap 就相當(dāng)于一個(gè)單鏈表顽冶,假如單鏈表有 n 個(gè)元素,遍歷的時(shí)間復(fù)雜度就是 O(n)殴胧,完全失去了它的優(yōu)勢(shì)渗稍。
針對(duì)這種情況,JDK 1.8 中引入了 紅黑樹(shù)(查找時(shí)間復(fù)雜度為 O(logn))來(lái)優(yōu)化這個(gè)問(wèn)題团滥。

shixinzhang

練習(xí)

leetcode no.599
使用哈希索引找最小索引

public class no599 {
    public static String[] findRestaurant(String[] list1, String[] list2) {
        List<String> buffer = null;
        Map<String, Integer> map1 = new HashMap<>();
        Map<String, Integer> map2 = new HashMap<>();
        int min = Integer.MAX_VALUE;
        for(int i = 0; i < list1.length; i++){
            map1.put(list1[i], i);
        }
        for (int i = 0; i < list2.length; i++){
            map2.put(list2[i], i);
        }

        for (int i = 0; i < list1.length; i++){
            if(map2.containsKey(list1[i])){
                int sum = map1.get(list1[i]) + map2.get(list1[i]);
                if(sum < min){
                    min = sum;
                    buffer = new ArrayList<String>();
                    buffer.add(list1[i]);
                }
                else if(sum == min)
                    buffer.add(list1[i]);
            }
        }
        return buffer.toArray(new String[buffer.size()]);
    }

    public static void main(String[] args){
        String[] a = {"Shogun", "Tapioca Express", "Burger King", "KFC"};
        String[] b = {"KFC", "Shogun", "Burger King"};
        System.out.println(Arrays.toString(findRestaurant(a, b)));
    }
}

no.594

public class no594 {
    public static int findLHS(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++){
//            if(!map.containsKey(nums[i]))
//                map.put(nums[i], 1);
//            else {
            int value = map.getOrDefault(nums[i], 0);
            value++;
            map.put(nums[i], value);
        }
        int length = 0;
        for (int num: map.keySet()){
            if(map.containsKey(num + 1))
                length = Math.max(length, map.get(num) + map.get(num + 1));
        }

        return length;
    }

    public static void main(String[] args){
        int[] nums = {1,3,2,2,5,2,3,7};
        System.out.println(findLHS(nums));
    }
}

http://www.cnblogs.com/chenssy/p/3521565.html

HashSet和HashMap的區(qū)別

  1. HashMap實(shí)現(xiàn)了Map接口 HashSet實(shí)現(xiàn)了Set接口
  2. HashMap儲(chǔ)存鍵值對(duì) HashSet僅僅存儲(chǔ)對(duì)象
  3. 使用put()方法將元素放入map中 使用add()方法將元素放入set中
  4. HashMap中使用鍵對(duì)象來(lái)計(jì)算hashcode值 HashSet使用成員對(duì)象來(lái)計(jì)算hashcode值竿屹,對(duì)于兩個(gè)對(duì)象來(lái)說(shuō)hashcode可能相同,所以equals()方法用來(lái)判斷對(duì)象的相等性灸姊,如果兩個(gè)對(duì)象不同的話拱燃,那么返回false
  5. HashMap比較快,因?yàn)槭鞘褂梦ㄒ坏逆I來(lái)獲取對(duì)象 HashSet較HashMap來(lái)說(shuō)比較慢
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末力惯,一起剝皮案震驚了整個(gè)濱河市碗誉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌父晶,老刑警劉巖哮缺,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異甲喝,居然都是意外死亡尝苇,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)糠溜,“玉大人淳玩,你說(shuō)我怎么就攤上這事》歉停” “怎么了蜕着?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)红柱。 經(jīng)常有香客問(wèn)我承匣,道長(zhǎng),這世上最難降的妖魔是什么豹芯? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任悄雅,我火速辦了婚禮,結(jié)果婚禮上铁蹈,老公的妹妹穿的比我還像新娘宽闲。我一直安慰自己,他們只是感情好握牧,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布容诬。 她就那樣靜靜地躺著,像睡著了一般沿腰。 火紅的嫁衣襯著肌膚如雪览徒。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天颂龙,我揣著相機(jī)與錄音习蓬,去河邊找鬼。 笑死措嵌,一個(gè)胖子當(dāng)著我的面吹牛躲叼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播企巢,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼枫慷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了浪规?” 一聲冷哼從身側(cè)響起或听,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎笋婿,沒(méi)想到半個(gè)月后誉裆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缸濒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年找御,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了元镀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡霎桅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出讨永,到底是詐尸還是另有隱情滔驶,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布卿闹,位于F島的核電站揭糕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏锻霎。R本人自食惡果不足惜著角,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望旋恼。 院中可真熱鬧吏口,春花似錦、人聲如沸冰更。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蜀细。三九已至舟铜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奠衔,已是汗流浹背谆刨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留归斤,地道東北人痊夭。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像官册,于是被迫代替她去往敵國(guó)和親生兆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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

  • 實(shí)際上膝宁,HashSet 和 HashMap 之間有很多相似之處鸦难,對(duì)于 HashSet 而言,系統(tǒng)采用 Hash 算...
    曹振華閱讀 2,513評(píng)論 1 37
  • 幾天前员淫,一個(gè)正在瘋狂碼代碼的午后合蔽,釘釘上一個(gè)小伙伴問(wèn)我:“你知道HashMap是在什么時(shí)候做bucket的初始化的...
    miaoLoveCode閱讀 5,552評(píng)論 3 28
  • 寫(xiě)在開(kāi)始之前: Java為數(shù)據(jù)結(jié)構(gòu)中的映射定義了一個(gè)接口java.util.Map,此接口主要有四個(gè)常用的實(shí)現(xiàn)類介返,...
    VictorLiang閱讀 500評(píng)論 0 0
  • 過(guò)去的時(shí)代是門(mén)當(dāng)戶對(duì)。 現(xiàn)在的時(shí)代刃宵,其實(shí)也是門(mén)當(dāng)戶對(duì)衡瓶,不過(guò)是性格和思想上的門(mén)當(dāng)戶對(duì)。 你要嫁給一個(gè)人牲证,首先你要和他...
    貓黍閱讀 394評(píng)論 0 1
  • 6年前哮针,有個(gè)樓主來(lái)八卦發(fā)帖,說(shuō)她倒追了七年的男生坦袍,終于和她在一起了十厢。 故事從樓主上高中開(kāi)始,她參加完軍訓(xùn)回來(lái)捂齐,一眼...
    李鈴鐺閱讀 1,742評(píng)論 2 21