LinkedHashMap源码分析

来源:互联网 发布:淘宝卡片祝福语幽默 编辑:程序博客网 时间:2024/05/21 10:28

接上一节关于HashMap的分析:


LinkedHashMap继承HashMap,所以HashMap是LinkedHashMap的父类,HashMap有的方法LinkedHashMap都有,LinkedHashMap也会重写某些方法,我们知道,HashMap底层数据结构是数组和链表,那么LinkedHashMap会额外维护一个按照插入顺序排序的双向循环列表。


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

这是linkedhashmap其中的一个构造方法,这里注意accessOrder这个参数,默认为false,那就按照插入的顺序排序,依靠那个双向循环列表来实现,如果传入accessOrder为true时,那将按照LRU算法来实现。

 void recordAccess(HashMap<K,V> m) {            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;            if (lm.accessOrder) {                lm.modCount++;                remove();                addBefore(lm.header);            }        }

先来看这个重要的方法,这个方法是在哪里调用的呢,

public V get(Object key) {        Entry<K,V> e = (Entry<K,V>)getEntry(key);        if (e == null)            return null;       <span style="color:#ff0000;"> e.recordAccess(this);</span>        return e.value;    }

public V put(K key, V value) {        if (key == null)            return putForNullKey(value);        int hash = hash(key.hashCode());        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;                <span style="color:#ff0000;">e.recordAccess(this);</span>                return oldValue;            }        }        modCount++;        addEntry(hash, key, value, i);        return null;    }
put方法没有重写,所以put方法还是用的父类HashMap的put()方法,说明在put和get时会调用recordAccess方法,就是在访问元素时调用,我们接着看这个put方法,在里面调用了addEntry方法,所以linkedhashmap 重写了addEntry方法


 void addEntry(int hash, K key, V value, int bucketIndex) {        createEntry(hash, key, value, bucketIndex);        // Remove eldest entry if instructed, else grow capacity if appropriate        Entry<K,V> eldest = header.after;        if (removeEldestEntry(eldest)) {            removeEntryForKey(eldest.key);        } else {            if (size >= threshold)                resize(2 * table.length);        }    }

 void createEntry(int hash, K key, V value, int bucketIndex) {        HashMap.Entry<K,V> old = table[bucketIndex];        Entry<K,V> e = new Entry<>(hash, key, value, old);        table[bucketIndex] = e;        e.addBefore(header);        size++;    }
可以发现,addEntry调用了createEntry方法,这个就是加入元素的过程,显然,这里加入了两次,在hash数组中加入,还要addBefore,即在双向循环列表中加入,加在头结点之后,即元素末尾。接下来再来看这个方法:
 void recordAccess(HashMap<K,V> m) {            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;            if (lm.accessOrder) {                lm.modCount++;                remove();                addBefore(lm.header);            }        }

如果accessOrder为true,调用remove移除这个entry,并且把这个加到双向链表的末尾,这样就实现了LRU。

再来看这几行代码:

 Entry<K,V> eldest = header.after;        if (<span style="color:#ff0000;">removeEldestEntry(eldest)</span>) {            removeEntryForKey(eldest.key);        } else {            if (size >= threshold)                resize(2 * table.length);        }

 protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {        return false;    }
默认是返回false,如果我们自己实现的话,可以通过重写这个方法来设置删除头结点之后的元素,即最老元素。


看来,通过源码还是可以学习好多东西的。



0 0