LinkedHashMap jdk1.8源碼解析

1. 特點

  1. 繼承HashMap

  2. Entry繼承HashMap的Node

     static class Entry<K, V> extends HashMap.Node<K, V> {
         Entry<K, V> before, after;
         
         Entry(int hash, K key, V value, Node<K, V> next) {
             super(hash, key, value, next);
         }
     }
    
  3. accessOrder:true訪問順序,false插入順序,測試代碼如下

     public static void main(String[] args) {
     	LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(0, 0.75f, true);// false
     	map.put("1", "value1");
     	map.put("2", "value2");
     	map.put("3", "value3");
     	print(map);
     	map.get("2");
     	//map.getOrDefault("2","default");
     	print(map);
     }
    
     accessOrder = true
    
     1	value1
     2	value2
     3	value3
     --------------------------------------
     1	value1
     3	value3
     2	value2
     --------------------------------------
    
     accessOrder = false
    
     1	value1
     2	value2
     3	value3
     --------------------------------------
     1	value1
     2	value2
     3	value3
     --------------------------------------
    

    如果爲false,get之後Entry存儲順序還是put的順序,如果爲true擇在get之後會把get的Entry放到隊列尾部。

2. 構造方法

public LinkedHashMap(int initialCapacity, float loadFactor) {
    super(initialCapacity, loadFactor);
    this.accessOrder = false;
}

public LinkedHashMap(int initialCapacity) {
    super(initialCapacity);
    this.accessOrder = false;
}

public LinkedHashMap() {
    super();
    this.accessOrder = false;
}

public LinkedHashMap(Map<? extends K, ? extends V> m) {
    super();
    this.accessOrder = false;
    this.putMapEntries(m, false);
}

// 實現Lru效果要實現3個參數構造方法
public LinkedHashMap(int initialCapacity,
                     float loadFactor,
                     boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

3. 增

LinkedHashMap的put直接調用了HashMap的put方法。

3.1 HashMap的put方法中newNode方法

put方法把一個Entry put到Map中,在HashMap中調用newNode生成的是HashMap.Node,在LinkedHashMap中newNode生成的是LinkedHashMap.Entry,這裏就是爲啥LinkeHashMap的afterNodeAccess中Node<K, V> e 強轉成 (LinkedHashMap.Entry<K, V>) e 不報錯的原因。

if ((p = tab[i = (n - 1) & hash]) == null) {
        tab[i] = this.newNode(hash, key, value, null);
}else{
	...
	this.newNode(hash, key, value, null);
}  


// HashMap中實現的該方法
Node<K, V> newNode(int hash, K key, V value, Node<K, V> next) {
    return new Node<>(hash, key, value, next);
}

// LinkedHashMap中實現的該方法
Node<K, V> newNode(int hash, K key, V value, Node<K, V> e) {
    LinkedHashMap.Entry<K, V> p =
            new LinkedHashMap.Entry<K, V>(hash, key, value, e);
	// 把生成的節點放到尾部 詳見3.2
    this.linkNodeLast(p);
    return p;
}
3.2 linkNodeLast

把新生成的Node放到鏈表尾部

// link at the end of list
private void linkNodeLast(LinkedHashMap.Entry<K, V> p) {
    LinkedHashMap.Entry<K, V> last = this.tail;
    this.tail = p;
    if (last == null) {
        this.head = p;
    } else {
        p.before = last;
        last.after = p;
    }
}
3.3 HashMap的put方法中的this.afterNodeAccess(e)

下面是HashMap的put方法中的代碼片段:

if (e != null) { // existing mapping for key
    V oldValue = e.value;
    if (!onlyIfAbsent || oldValue == null) {
        e.value = value;
    }
    this.afterNodeAccess(e);
    return oldValue;
}

其中調用了this.afterNodeAccess(e),在HashMap中這是一個空方法,LinkedHashMap中實現了這個方法如下 :

@Override
void afterNodeAccess(Node<K, V> e) { // move node to last
    LinkedHashMap.Entry<K, V> last;
    if (this.accessOrder && (last = this.tail) != e) {
        LinkedHashMap.Entry<K, V> p =
                (LinkedHashMap.Entry<K, V>) e;
        LinkedHashMap.Entry<K, V> b = p.before;
        LinkedHashMap.Entry<K, V> a = p.after;
        p.after = null;
        if (b == null) {
            this.head = a;
        } else {
            b.after = a;
        }
        if (a != null) {
            a.before = b;
        } else {
            last = b;
        }
        if (last == null) {
            this.head = p;
        } else {
            p.before = last;
            last.after = p;
        }
        this.tail = p;
        ++this.modCount;
    }
}

從註釋可以看出,將Node移動到鏈表尾部 , 併爲LinkedHashMap.Entry的after和before賦值。

4. 查

@Override
public V get(Object key) {
    Node<K, V> e;
    if ((e = this.getNode(hash(key), key)) == null) {
        return null;
    }
    if (this.accessOrder) {
		// 詳見3.2
        this.afterNodeAccess(e);
    }
    return e.value;
}

如果設置了accessOrder爲訪問順序,那麼會把get到的Entry移動到尾部。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章