04_LinkedHashMap源码剖析

一、基本概念

  1. LinkedHashMap继承HashMap,所以源码并不多,大部分逻辑会复用父类的方法,LinkedHashMap实现了元素有序的功能
  2. LinkedHashMap底层是基于链表来实现的,链表会通过头元素,尾元素,pre,next指针来实现有序map。TreeMap是基于红黑树来实现顺序的
  3. LinkedHashMap原则上来说一些基本的原理和操作跟HashMap是差不多的,唯一主要的区别就是你在插入、覆盖、删除的时候会记录一下key-value对的顺序,用一个链表来记录,在遍历的时候,就可以按照这个顺序来遍历

二、源码

调用LinkedHashMap的put方法的时候,会先进入到HashMap的put方法中

代码片段一、HashMap.put()

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

/**
 * Implements Map.put and related methods
 *
 * @param hash hash for key
 * @param key the key
 * @param value the value to put
 * @param onlyIfAbsent if true, don't change existing value
 * @param evict if false, the table is in creation mode.
 * @return previous value, or null if none
 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node[] tab; Node p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // 如果说这里的tab[i] != null的话,对于LinkedHashMap其实就是相当于覆盖key
    if ((p = tab[i = (n - 1) & hash]) == null)
        // 这里走到LinkedHashMap中的newNode方法中
        tab[i] = newNode(hash, key, value, null);
    else {
        Node 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)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);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        // 如果覆盖key的话,那么会走这段逻辑
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            // 这里走到LinkedHashMap的afterNodeAccess逻辑中
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

代码片段二、LinkedHashMap.afterNodeInsertion

Node newNode(int hash, K key, V value, Node e) {
    // 使用LinkedHashMap构造函数,构造一个LinkedHashMap一个Entry节点
    LinkedHashMap.Entry p =
        new LinkedHashMap.Entry(hash, key, value, e);
    linkNodeLast(p);
    return p;
}

// 将节点放入到集合的最后位置
// link at the end of list
private void linkNodeLast(LinkedHashMap.Entry p) {
    // last指针指向集合的最后一个节点,如图一、
    LinkedHashMap.Entry last = tail;
    // 执行后如图二
    tail = p;
    if (last == null)
        head = p;
    else {
        // 执行后如图三、
        p.before = last;
        // 图四、
        last.after = p;
    }
}

LinkedHashMap.Entry last = tail;执行后如图下图

LinkedHashMap-图一 .png

tail = p

LinkedHashMap-图二.png

p.befor = last

LinkedHashMap-图三.png

last.after = p;

LinkedHashMap-图四.png

通过上面几个步骤,就可以将一个新的节点挂在了链表的尾部

三、如果覆盖某个key,那么他在链表中的顺序是否会被改变?

  1. 如果我们是做key值的覆盖,多次覆盖一个值,默认是不会改变他的顺序,LinkedHashMap有一个参数的accessOrder,可以在构造的时候传入进去,默认他是false,如果是默认为false的话,那么你比如说你get一个key,或者是覆盖这个key的值,都不会改变他在链表里的顺序。
  2. 但是如果accessOrder是true的话,那么如果get一个key,或者是put覆盖这个key的值,就会导致个key-value对顺序会在链表里改变,他会被挪动到链表的尾部去,如果你把accessOrder指定为true,你每次修改一个key的值,或者是get访问一下这个key,都会导致这个key挪动到链表的尾部去

代码片段三、

void afterNodeAccess(Node e) { // move node to last
    LinkedHashMap.Entry last;
    if (accessOrder && (last = tail) != e) {
        LinkedHashMap.Entry p =
            (LinkedHashMap.Entry)e, b = p.before, a = p.after;
        p.after = null;
        if (b == null)
            head = a;
        else
            b.after = a;
        if (a != null)
            a.before = b;
        else
            last = b;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
        tail = p;
        ++modCount;
    }
}
// 入股爱accessOrder设置为true,那么get操作也会影响他的顺序
public V get(Object key) {
    Node e;
    if ((e = getNode(hash(key), key)) == null)
        return null;
    if (accessOrder)
        afterNodeAccess(e);
    return e.value;
}

总结:

  1. 因为LinkedHashMap基础HashMap,所以HashMap有的特性,LinkedHashMap也都有
  2. 影响LinkedHashMap元素顺序的关键属性accessOrder,默认false,不管是put覆盖Key还是get操作,都不会影响LinkedHashMap元素的顺序

你可能感兴趣的:(04_LinkedHashMap源码剖析)