LinkedHashMap浅析

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

/**
     * The head of the doubly linked list.
     */
    private transient Entry<K,V> header;

    /**
     * The iteration ordering method for this linked hash map: <tt>true</tt>
     * for access-order, <tt>false</tt> 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<? extends K, ? extends V> 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<K,V> eldest = header.after;
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        }
    }
  • transfer
void transfer(HashMap.Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        //遍历整个双向链表进行数据移动
        for (Entry<K,V> 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;
  • 再进行以上几步操作,最终会生成黑色箭头的链表能够串起来

在这里插入图片描述
从网上找了一个图片,供大家理解
在这里插入图片描述

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

get浅析

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

public V get(Object key) {
		//调用父类的获取如果有则返回,若没有则返回null
        Entry<K,V> e = (Entry<K,V>)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是插入效率快,查询效率慢;
    以上是个人的理解,如有不妥也请各位大咖指正。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨香异境

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值