天天看點

Map接口實作類總結一、場景選擇二、HashMap專題三、key-value是否為null四、HashMap源碼分析jdk1.7和jdk1.8的差別

一、場景選擇

TreeMap和HashMap選擇

排序:排序的時候用TreeMap,其他用HashMap。

為什麼後者性能更好?

個人了解,排序需要犧牲性能,是以一般不排序都用後者。

LinkedHashMap和HashMap選擇

FIFO:插入疊代順序一緻用LinkedHashMap,其他用HashMap

LinkedHashMap 繼承HashMap底層連結清單結構,插入順序與疊代順序一緻

Hashtable(棄用)

線程安全,不推薦使用,使用ConcurrentHashMap代替多線程場景,性能更好。

ConcurrentHashMap

3個字:多線程

二、HashMap專題

特點:key不重複

ArrayList 底層是數組結構, LinkedList底層是連結清單結構

hashmap底層結構?

HashMap結合以上兩點,數組+單連結清單結構

什麼是bucket?

bucket可以了解為對象的存儲空間/位址。

hashmap是怎麼插入資料,插入資料的過程?

通過鍵值對(entry)對象計算hashcode 确定插入bucket位置,存儲entry對象,hashcode相同,bucket就相同,發生沖突,通過單連結清單來解決碰撞問題,如果碰撞就存到連結清單的下一個節點。

hashmap怎麼取資料?

通過get方法,使用鍵值對象的hashcode去找bucket位置,找到bucket位置之後,會調用keys.equals()方法去找到連結清單中正确的節點,最終找到要找的值對象。

hashmap怎麼實作同步?

HashMap可以通過下面的語句進行同步:

Map m = Collections.synchronizeMap(hashMap);

什麼是碰撞?為什麼String Integer作為key?

碰撞就是通過hash算法計算出來的hashcode如果一樣,就是發生存儲位址碰撞。而String是不可變的,重寫了hashcode equals方法,比較适合做key。

為什麼hashmap是線程不安全的?

因為如果兩個線程put時,key的hash值一樣,而hashmap時根據hashcode值存儲的,就會存到同一個位置,出現資料覆寫。

hashmap初始數組大小,如何擴容?

初始16的數組,容量達到3/4的時候2倍擴容,16*0.75=12   16*2=32   0~31

三、key-value是否為null

可以搞個表格,但是不想去翻表格,怎麼記?總不能每次突然問你,你都去翻表格吧

key: hashmap 可空,其他都不行。(TreeMap/ConcurrentHashMap/HashTable)。 linkedhashmap就是繼承hashmap的,記一個就夠了。

value:hashmap/TreeMap可空。

口訣:H2T控   =   hashmap  keyvalue可空, treemap  value 可空,key排序肯定不能為null

哈哈哈哈

四、HashMap源碼分析

put操作

Map<String, Object> map = new HashMap<>();

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)//初始化時, putVal中的table==null,進入這裡,調用resize,resize會初始化容量為16,table也會有值
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)//判斷容量内是否指派
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)//是否是紅黑樹
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);//JDK1.8容量大于=8的時候使用紅黑樹,否則使用連結清單
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
           
Map接口實作類總結一、場景選擇二、HashMap專題三、key-value是否為null四、HashMap源碼分析jdk1.7和jdk1.8的差別

jdk1.7和jdk1.8的差別

(1)初始數組

jdk7:在建立hashmap對象的時候,就初始table的容量為16

jdk8:在建立hashmap對象的時候,并沒有初始table,就初始了加載因子,隻有當我們第一次添加的時候,才會初始table的容量為16

(2)table類型

jdk7:table的類型為Entry

jdk8:table的類型為Node,為了轉為樹結構

(3)結構

jdk7:哈希表為數組+連結清單,不管連結清單的總結點數為多少,都不會變成樹結構

jdk8:哈希表為數組+連結清單+紅黑樹,連結清單的節點數(沖突節點數)>=8 &&

桶的總個數(table的容量,也就是我們開發中map的大小了) >= 64的時候,會講連結清單結構(沖突的連結清單結構)變成紅黑樹TreeNode