LinkedHashMap是如何组织数据的

LinkedHashMap简介

LinkedHashMap继承自HashMap,底层仍然是数组加链表,除了发生冲突后的单链表,它还维护了一个链接所有元素的双链表,这样就能记录元素的插入或者访问顺序了。所以LinkedHashMap可以做到有序迭代,也可以作为LRU算法的基石。

数据结构

LinkedEntry<K, V> header;
static class LinkedEntry<K, V> extends HashMapEntry<K, V> {
       LinkedEntry<K, V> nxt;
       LinkedEntry<K, V> prv;
       /** Create the header entry */
       LinkedEntry() {
           super(null, null, 0, null);
           nxt = prv = this;
       }
       /** Create a normal entry */
       LinkedEntry(K key, V value, int hash, HashMapEntry<K, V> next,
                   LinkedEntry<K, V> nxt, LinkedEntry<K, V> prv) {
           super(key, value, hash, next);
           this.nxt = nxt;
           this.prv = prv;
       }
   }

LinkedEntry继承自HashMapEntry
next属性仍然组织发生冲突后的单链表。
nxt和prv两个属性组织一个所有元素的双链表。这样就能记录插入顺序或者访问顺序。
header是双向循环链表的起点,和HashMap中的entryForNullKey一样,它游离于table之外。
LinkedHashMap其实存储了两套逻辑,一套是原封不动的HashMap的逻辑。
另一套是一个双向链表的逻辑。

put方法

LinkedHashMap并没有重写HashMap的put方法,而只是重写了其中最后的addNewEntry方法。
因为除了组织双向链表的逻辑,其他地方都和HashMap一样。

void addNewEntry(K key, V value, int hash, int index) {
        LinkedEntry<K, V> header = this.header;

        // Remove eldest entry if instructed to do so.
        LinkedEntry<K, V> eldest = header.nxt;
        // 默认的removeEldestEntry直接返回了false
        if (eldest != header && removeEldestEntry(eldest)) {
            remove(eldest.key);
        }

        // Create new entry, link it on to list, and put it into table
        LinkedEntry<K, V> oldTail = header.prv;
        LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
                key, value, hash, table[index], header, oldTail);
        table[index] = oldTail.nxt = header.prv = newTail;
    }

第叉叉行,无非也就是一个双向链表的插入方法。如果这个过程不熟悉,去看一LinkedList里的add方法就豁然开朗了。

makeTail方法

LinkedHashMap的默认顺序是插入顺序,如果要实现访问顺序,那么需要将全局的accessOrder属性置为true。
当然LinkedHashMap提供了带accessOrder参数的构造函数。访问顺序是在调用get等方法的时候内部调用了makeTail方法,
非常直观,也就是把要访问的Entry放到尾部去。

private void makeTail(LinkedEntry<K, V> e) {
       // 把e从链表里拿出来
       e.prv.nxt = e.nxt;
       e.nxt.prv = e.prv;

       // 把e链接在尾部
       LinkedEntry<K, V> header = this.header;
       LinkedEntry<K, V> oldTail = header.prv;
       e.nxt = header;
       e.prv = oldTail;
       oldTail.nxt = header.prv = e;
       modCount++;
   }

makeTail方法分两步实现,第一是把元素取出来,第二是插入到尾部。虽然这样写非常像一段台词,
但实际情况就是这样

get方法每次都调用,put方法中如果key相同会替换掉value,在替换之前会调用preModify方法,
preModify在LinkedHashMap中的实现是调用这个方法

关于双向链表的删除和插入操作,请看。


public class LinkedHashMap<K, V> extends HashMap<K, V> {

    // 头Entry,不包含任何数据。
    transient LinkedEntry<K, V> header;
    // true是访问排序,false是按插入顺序排。
    private final boolean accessOrder;
    // 构造函数,默认是插入顺序。
    public LinkedHashMap() {
        init();
        accessOrder = false;
    }
// 指定容量
    public LinkedHashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

// 指定容量和加载因子,内部调用三个参数的构造函数。
// 三个参数的构造函数调用 super(initialCapacity, loadFactor) 也就是HashMap的构造函数
// loadFactor会被忽略掉,然后自动使用0.75的。

    public LinkedHashMap(int initialCapacity, float loadFactor) {
        this(initialCapacity, loadFactor, false);
    }

// 调用HashMap的构造方法,来构造table数组
    public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
        super(initialCapacity, loadFactor);
        init();
        this.accessOrder = accessOrder;
    }

    @Override void init() {
        header = new LinkedEntry<K, V>();
    }

    //  双向链表的数据结构,这个感觉和LinkedList的双向链表感觉感谢
    static class LinkedEntry<K, V> extends HashMapEntry<K, V> {
        LinkedEntry<K, V> nxt;
        LinkedEntry<K, V> prv;

        /** Create the header entry */
        LinkedEntry() {
            super(null, null, 0, null);
            nxt = prv = this;
        }

        /** Create a normal entry */
        LinkedEntry(K key, V value, int hash, HashMapEntry<K, V> next,
                    LinkedEntry<K, V> nxt, LinkedEntry<K, V> prv) {
            //调用super后,发生了什么?
            super(key, value, hash, next);
            this.nxt = nxt;
            this.prv = prv;
        }
    }

// 这个this是什么意思
            final K key;
            V value;
            final int hash;
            HashMapEntry<K, V> next;

            HashMapEntry(K key, V value, int hash, HashMapEntry<K, V> next) {
                this.key = key;
                this.value = value;
                this.hash = hash;
                this.next = next;
            }
    /**
     * Returns the eldest entry in the map, or {@code null} if the map is empty.
     * @hide
     */
    public Entry<K, V> eldest() {
        LinkedEntry<K, V> eldest = header.nxt;
        return eldest != header ? eldest : null;
    }

    //  新add的是在尾部
    @Override void addNewEntry(K key, V value, int hash, int index) {
        LinkedEntry<K, V> header = this.header;

        // Remove eldest entry if instructed to do so.
        LinkedEntry<K, V> eldest = header.nxt;
        if (eldest != header && removeEldestEntry(eldest)) {
            remove(eldest.key);
        }

        // Create new entry, link it on to list, and put it into table
        LinkedEntry<K, V> oldTail = header.prv;
        LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
                key, value, hash, table[index], header, oldTail);
        table[index] = oldTail.nxt = header.prv = newTail;
    }

    @Override void addNewEntryForNullKey(V value) {
        LinkedEntry<K, V> header = this.header;

        // Remove eldest entry if instructed to do so.
        LinkedEntry<K, V> eldest = header.nxt;
        if (eldest != header && removeEldestEntry(eldest)) {
            remove(eldest.key);
        }

        // Create new entry, link it on to list, and put it into table
        LinkedEntry<K, V> oldTail = header.prv;
        LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
                null, value, 0, null, header, oldTail);
        entryForNullKey = oldTail.nxt = header.prv = newTail;
    }

    /**
     * As above, but without eviction.
     */
    @Override HashMapEntry<K, V> constructorNewEntry(
            K key, V value, int hash, HashMapEntry<K, V> next) {
        LinkedEntry<K, V> header = this.header;
        LinkedEntry<K, V> oldTail = header.prv;
        LinkedEntry<K, V> newTail
                = new LinkedEntry<K,V>(key, value, hash, next, header, oldTail);
        return oldTail.nxt = header.prv = newTail;
    }

    /**
     * Returns the value of the mapping with the specified key.
     *
     * @param key
     *            the key.
     * @return the value of the mapping with the specified key, or {@code null}
     *         if no mapping for the specified key is found.
     */
    //  get方法复写
    @Override public V get(Object key) {
        if (key == null) {
            HashMapEntry<K, V> e = entryForNullKey;
            if (e == null)
                return null;
            if (accessOrder)
                makeTail((LinkedEntry<K, V>) e);
            return e.value;
        }

        int hash = Collections.secondaryHash(key);
        // 索引和HashMap一样
        HashMapEntry<K, V>[] tab = table;
        for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
                e != null; e = e.next) {
            K eKey = e.key;
            if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                if (accessOrder)
                // 放到队尾
                    makeTail((LinkedEntry<K, V>) e);
                return e.value;
            }
        }
        return null;
    }

    /**
     * Relinks the given entry to the tail of the list. Under access ordering,
     * this method is invoked whenever the value of a  pre-existing entry is
     * read by Map.get or modified by Map.put.
     */
    private void makeTail(LinkedEntry<K, V> e) {
        // Unlink e
        //  把e从链表里拿出来
        e.prv.nxt = e.nxt;
        e.nxt.prv = e.prv;

        // Relink e as tail
        //  然后把e放到尾部
        LinkedEntry<K, V> header = this.header;
        LinkedEntry<K, V> oldTail = header.prv;
        e.nxt = header;
        e.prv = oldTail;
        oldTail.nxt = header.prv = e;
        modCount++;
    }

    @Override void preModify(HashMapEntry<K, V> e) {
        if (accessOrder) {
            makeTail((LinkedEntry<K, V>) e);
        }
    }

    @Override void postRemove(HashMapEntry<K, V> e) {
        LinkedEntry<K, V> le = (LinkedEntry<K, V>) e;
        le.prv.nxt = le.nxt;
        le.nxt.prv = le.prv;
        le.nxt = le.prv = null; // Help the GC (for performance)
    }

//
    public void clear() {
      // HashMap的clear方法
      // 把table里都装上null
        super.clear();

        // Clear all links to help GC
        // 从header开始遍历清空
        LinkedEntry<K, V> header = this.header;
        for (LinkedEntry<K, V> e = header.nxt; e != header; ) {
            LinkedEntry<K, V> nxt = e.nxt;
            e.nxt = e.prv = null;
            e = nxt;
        }
        // 回归到初始的空map状态
        header.nxt = header.prv = header;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值