LinkedHashMap源码剖析~

包路径:package java.util;

                 import java.io.*;

一、继承关系

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>{
}

继承于HashMap,那么HashMap中所有的非private方法都被继承下来;实现了Map接口。

二、基本属性

1、private static final long serialVersionUID = 3801124242820219131L;

2、private transient Entry<K,V> header;  //链表的头节点

3、private final boolean accessOrder;

三、构造方法(共提供了五种构造方法)

构造方法一、传入两个参数:初始容量的大小、加载因子,调用父类的构造方法,按照插入顺序
public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

构造方法二、传入一个参数:初始容量大小,调用父类的构造方法,取得键值对的顺序是插入顺序
 public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

构造方法三、无参构造方法,调用父类的构造方法,默认插入顺序
public LinkedHashMap() {
        super();
        accessOrder = false;
    }

构造方法四、通过传入的Map创建一个LinkedHashMap
public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super(m);
        accessOrder = false;
    }

构造方法五、传入三个参数:初始容量大小、加载因子、键值对的保持顺序创建一个LinkedHashMap
public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

从构造方法可以知道:默认采用插入顺序来保持键值对的顺序,所有的构造方法都会调用父类的构造方法。

四、核心方法

1、添加元素:put()------>调用父类的添加元素方法

public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)  key为null的情况
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this); 使用自己(LinkedHashMap)实现的方法
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i); 使用自己(LinkedHashMap)实现的方法
        return null;
    }
子类重写的方法:
void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; 将传进去的HashMap强转
            if (lm.accessOrder) { 默认为false,但是当为true的时候,即按照访问顺序
                lm.modCount++;
                remove(); 移除当前节点
                addBefore(lm.header); 将当前节点插入到头结点之前的位置
            }
        }

private void addBefore(Entry<K,V> existingEntry) {
            after  = existingEntry;
            before = existingEntry.before;
            before.after = this;
            after.before = this;
        }


void addEntry(int hash, K key, V value, int bucketIndex) {
        super.addEntry(hash, key, value, bucketIndex); 首先调用父类的方法
        Entry<K,V> eldest = header.after;
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        }
    }

protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }

图例演示:这里参考了网上一位博主的博客:https://blog.csdn.net/ShelleyLittlehero/article/details/82954474

2、获取元素:get()

public V get(Object key) {
        Entry<K,V> e = (Entry<K,V>)getEntry(key); 调用父类的方法,取得要查找的元素
        if (e == null)
            return null;
        e.recordAccess(this); 记录访问顺序
        return e.value;
    }

3、删除元素:remove()

private void remove() { 移除节点,修改前后引用
            before.after = after;
            after.before = before;
        }

AccessOrder作用于那些方法?

->put()[在节点重复是起作用]

->get()若为false,不改变双向链表结构,若为true:将当前节点从header组织的双向链表删除、放在header的before位置

五、LinkedHashMap的特点总结

1、底层数据结构是双向循环链表+数组

private static class Entry<K,V> extends HashMap.Entry<K,V> {
       
        Entry<K,V> before, after;

      Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
            super(hash, key, value, next);
     }

Entry的基本属性:K key、V value、Entry<K,V> next、int hash---->继承自HashMap.Entry<K,V>

                               Entry<K,V> before、  Entry<K,V> after------->LinkedHashMap特有的属性

注意:next是用来维护HashMap指定数组位置上连接的Entry的顺序;before、after是用来维护Entry插入的先后顺序的。

2、保证数据的插入有序和访问有序。有序性是通过属性accessOrder(布尔类型)来标识的,默认是插入有序(accessOrder=false),访问有序是(accessOrder=true)。

3、entry类型的header,内部没有存储数据。

4、继承于HashMap,也是非线程安全的,具有HashMap的特点,在这里不再叙述。

5、适用场景:当需要保证数据是有序的时候,可以使用该类

猜你喜欢

转载自blog.csdn.net/qq_40303781/article/details/84546229