LinkedHashMap jdk1.8源码解析

1. 特点

  1. 继承HashMap

  2. Entry继承HashMap的Node

     static class Entry extends HashMap.Node {
         Entry before, after;
         
         Entry(int hash, K key, V value, Node next) {
             super(hash, key, value, next);
         }
     }
    
  3. accessOrder:true访问顺序,false插入顺序,测试代码如下

     public static void main(String[] args) {
     	LinkedHashMap map = new LinkedHashMap(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 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 e 强转成 (LinkedHashMap.Entry) 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 newNode(int hash, K key, V value, Node next) {
    return new Node<>(hash, key, value, next);
}

// LinkedHashMap中实现的该方法
Node newNode(int hash, K key, V value, Node e) {
    LinkedHashMap.Entry p =
            new LinkedHashMap.Entry(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 p) {
    LinkedHashMap.Entry 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 e) { // move node to last
    LinkedHashMap.Entry last;
    if (this.accessOrder && (last = this.tail) != e) {
        LinkedHashMap.Entry p =
                (LinkedHashMap.Entry) e;
        LinkedHashMap.Entry b = p.before;
        LinkedHashMap.Entry 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 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移动到尾部。

你可能感兴趣的:(Java源码解读)