LinkedHashMap浅析

LinkedHashMap是数组+双向链表的数据结构,继承HashMap并重写部分方法来达到双向链表的特性。
LinkedHashMap浅析_第1张图片
LindedHashMap重写了HashMap的Entry,使其具有双向链表的特性,自身维护的属性只有两个属性,其他均继承自HashMap。

/**
     * The head of the doubly linked list.
     */
    private transient Entry header;

    /**
     * The iteration ordering method for this linked hash map: true
     * for access-order, false for insertion-order.
     *
     * @serial
     */
    private final boolean accessOrder;

构造方法

LinkedHashMap的构造方法都是调用父类即HashMap中相应的构造进行初始化,唯一不同的是在HashMap中的init()没有作用,而LinkedHashMap重写了该方法进行初始化双向链表的特性。

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

这几个构造就不做分析了,看下init()方法

void init() {
        header = new Entry<>(-1, null, null, null);
        header.before = header.after = header;
}

这里初始化了一个空的头节点header。before和after都指向自己。

put浅析

LinkedHashMap中没有重写put,采用父类即HashMap中的方法,但对其中的方法进行重写使其适用自身特性。
关于HashMap的分析可以参考HashMap浅析

  • addEntry
void addEntry(int hash, K key, V value, int bucketIndex) {
		//调用父类方法进行数据添加,涉及一些方法重新往下看
        super.addEntry(hash, key, value, bucketIndex);
        // Remove eldest entry if instructed
        Entry eldest = header.after;
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        }
    }
  • transfer
void transfer(HashMap.Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        //遍历整个双向链表进行数据移动
        for (Entry e = header.after; e != header; e = e.after) {
            if (rehash)
                e.hash = (e.key == null) ? 0 : hash(e.key);
            int index = indexFor(e.hash, newCapacity);
            //注意这里是next而不是after
            e.next = newTable[index];
            newTable[index] = e;
        }
}

举例讲解下上述代码的过程。如图中第一行header、Entry(B)、Entry©,第二行为新数组的下边位置(下边为重新计算)

  • 首先for循环将header的after节点即B给e(中间变量),然后判断是否为header节点,不是则进入循环内部。
  • 重新计算index;
  • 将e.next指向新数组的对应下边位置,即图中红色箭头部分;
  • 将新数组index位置赋值e即B;
  • 再循环将e.after即C指定变量e,注意此处是e.after而不是e.next;
  • 再进行以上几步操作,最终会生成黑色箭头的链表能够串起来

LinkedHashMap浅析_第2张图片
从网上找了一个图片,供大家理解
LinkedHashMap浅析_第3张图片

  • createEntry
void createEntry(int hash, K key, V value, int bucketIndex) {
		//将当前位置数据放到中间变量old中。
        HashMap.Entry old = table[bucketIndex];
        //新创建并将old放到e.next上
        Entry e = new Entry<>(hash, key, value, old);
        table[bucketIndex] = e;
        //将新增加的节点放到链表的尾部
        e.addBefore(header);
        size++;
}
//将新节点放到existingEntry(header)前边即链表的尾部
private void addBefore(Entry existingEntry) {
            after  = existingEntry;
            before = existingEntry.before;
            before.after = this;
            after.before = this;
}   

get浅析

相较于put,get方式就比较简单了,LinkedHashMap重写了get方法

public V get(Object key) {
		//调用父类的获取如果有则返回,若没有则返回null
        Entry e = (Entry)getEntry(key);
        if (e == null)
            return null;
        e.recordAccess(this);
        return e.value;
}

此时存在两种情况:

  • key对应的value为null;
  • Map中没有对应的值;

总结

HashMap与LinkedHashMap的区别:

  • HashMap是数组+单链表的方式,LinkedHashMap是数组+双向链表;
  • HashMap是无序的,LinkedHashMap是有序的;
  • HashMap查询效率快,插入效率慢,LinkedHashMap是插入效率快,查询效率慢;
    以上是个人的理解,如有不妥也请各位大咖指正。

你可能感兴趣的:(java)