集合之LinkedHashMap(含JDK1.8源码分析)

一、前言

  大多数的情况下,只要不涉及线程安全问题,map都可以使用hashMap,不过hashMap有一个问题,hashMap的迭代顺序不是hashMap的存储顺序,即hashMap中的元素是无序的。但是有些场景下,我们需要使用一个有序的map。这种情况下,我们就需要使用linkedHashMap,它虽然增加了时间和空间上的开销,但是通过维护一个运行于所有条目的双向链表,linkedHashMap保证了元素的迭代顺序。

  四个关注点在linkedHashMap上的答案

二、linkedHashMap的数据结构

  说明:上图说明了linkedHashMap的数据结构,和hashMap一样,linkedHashMap也使用了数组+链表(单向链表)+红黑树的方式来存储元素,所不同的是,因为需要维护元素的存储顺序,linkedHashMap还使用了双向链表来将前后元素串起来。所以linkedHashMap的数据结构为:数组+单向链表+红黑树+双向链表。

三、linkedHashMap源码分析-属性及构造函数

  3.1 类的继承关系

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

  说明:linkedHashMap继承自hashMap,所以hashMap中非私有的属性和方法,linkedHashMap都可以使用,而且从下图linkedHashMap的方法中可以看出,linkedHashMap中并没有直接操作数据结构的方法(比如对元素的增删改查的函数,除了get函数),其对元素的增删改查等操作都是基于其父类hashMap中的方法,所不同的只是细节上的实现有所区别罢了。

  linkedHashMap中的方法:

  3.2 类的属性

public class LinkedHashMap<K,V>  extends HashMap<K,V> implements Map<K,V> {
    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }
    // 版本序列号
    private static final long serialVersionUID = 3801124242820219131L;

    // 链表头结点
    transient LinkedHashMap.Entry<K,V> head;

    // 链表尾结点
    transient LinkedHashMap.Entry<K,V> tail;

    // 访问顺序
    final boolean accessOrder;
}

  说明:其中静态内部类Entry就是存储元素的地方,类似于hashMap中的Node,只是多了个before,after属性用于维护双向链表。accessOrder为true的时候,linkedHashMap中的元素是以访问的顺序存储,accessOrder为false的时候,linkedHashMap中的元素是以插入的顺序存储,一般我们使用的都是accessOrder为false的情况,即元素以插入的顺序排序,关于accessOrder为true的用法下面会提到。

  3.3 类的构造函数

  1、LinkedHashMap(int initialCapacity, float loadFactor)型

/**
     * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
     * with the specified initial capacity and load factor.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

  说明:调用父类hashMap中对应的构造方法,并将accessOrder置为false,通过指定的初始容量和加载因子实例化一个空的、以插入顺序来存储的linkedHashMap。

  2、LinkedHashMap(int initialCapacity)型

/**
     * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
     * with the specified initial capacity and a default load factor (0.75).
     *
     * @param  initialCapacity the initial capacity
     * @throws IllegalArgumentException if the initial capacity is negative
     */
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

  说明:调用父类hashMap中对应的构造方法,并将accessOrder置为false,通过指定的初始容量和默认的加载因子实例化一个空的、以插入顺序来存储的linkedHashMap。

  3、LinkedHashMap()型

/**
     * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
     * with the default initial capacity (16) and load factor (0.75).
     */
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }

  说明:调用父类hashMap中对应的构造方法,并将accessOrder置为false,通过默认的初始容量和加载因子实例化一个空的、以插入顺序来存储的linkedHashMap。

  4、LinkedHashMap(Map<? extends K, ? extends V> m)型

/**
     * Constructs an insertion-ordered <tt>LinkedHashMap</tt> instance with
     * the same mappings as the specified map.  The <tt>LinkedHashMap</tt>
     * instance is created with a default load factor (0.75) and an initial
     * capacity sufficient to hold the mappings in the specified map.
     *
     * @param  m the map whose mappings are to be placed in this map
     * @throws NullPointerException if the specified map is null
     */
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }

  说明:调用父类hashMap中对应的构造方法,并将accessOrder置为false,通过默认的初始容量和加载因子实例化一个以插入顺序来存储的linkedHashMap(已存储参数map中的元素)。

  5、LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)型


/**
     * Constructs an empty <tt>LinkedHashMap</tt> instance with the
     * specified initial capacity, load factor and ordering mode.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @param  accessOrder     the ordering mode - <tt>true</tt> for
     *         access-order, <tt>false</tt> for insertion-order
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

   说明:调用父类hashMap中对应的构造方法,并将accessOrder置为true或false,通过指定的初始容量、指定的加载因子、指定的accessOrder实例化一个空的linkedHashMap。

四、linkedHashMap源码分析-核心函数

  linkedHashMap对元素的增删改查等操作大多基于其父类hashMap中的方法,只是实现的细节有所不同,而linkedHashMap所做的是维护元素的插入顺序(双向链表)。

  4.1 增:put和putVal函数----存储元素

  所使用的是hashMap中的put和putVal函数

 1 /**
 2      * Associates the specified value with the specified key in this map.
 3      * If the map previously contained a mapping for the key, the old
 4      * value is replaced.
 5      *
 6      * @param key key with which the specified value is to be associated
 7      * @param value value to be associated with the specified key
 8      * @return the previous value associated with <tt>key</tt>, or
 9      *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
10      *         (A <tt>null</tt> return can also indicate that the map
11      *         previously associated <tt>null</tt> with <tt>key</tt>.)
12      */
13     public V put(K key, V value) {
14         return putVal(hash(key), key, value, false, true);
15     }

  但是在putVal中的实现有所不同

 1 /**
 2      * Implements Map.put and related methods
 3      *
 4      * @param hash hash for key
 5      * @param key the key
 6      * @param value the value to put
 7      * @param onlyIfAbsent if true, don't change existing value
 8      * @param evict if false, the table is in creation mode.
 9      * @return previous value, or null if none
10      */
11     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
12                    boolean evict) {
13         Node<K,V>[] tab; Node<K,V> p; int n, i;
14         if ((tab = table) == null || (n = tab.length) == 0)
15             n = (tab = resize()).length;
16         if ((p = tab[i = (n - 1) & hash]) == null)
17             tab[i] = newNode(hash, key, value, null);
18         else {
19             Node<K,V> e; K k;
20             if (p.hash == hash &&
21                 ((k = p.key) == key || (key != null && key.equals(k))))
22                 e = p;
23             else if (p instanceof TreeNode)
24                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
25             else {
26                 for (int binCount = 0; ; ++binCount) {
27                     if ((e = p.next) == null) {
28                         p.next = newNode(hash, key, value, null);
29                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
30                             treeifyBin(tab, hash);
31                         break;
32                     }
33                     if (e.hash == hash &&
34                         ((k = e.key) == key || (key != null && key.equals(k))))
35                         break;
36                     p = e;
37                 }
38             }
39             if (e != null) { // existing mapping for key
40                 V oldValue = e.value;
41                 if (!onlyIfAbsent || oldValue == null)
42                     e.value = value;
43                 afterNodeAccess(e);
44                 return oldValue;
45             }
46         }
47         ++modCount;
48         if (++size > threshold)
49             resize();
50         afterNodeInsertion(evict);
51         return null;
52     }

  说明1:注意位于第17、28行的newNode方法,因为linkedHashMap中重写了这个方法,所以在虽然存储元素用的是hashMap中的putVal方法,但到newNode时调用的是linkedHashMap的newNode方法,这是多态的概念。

  linkedHashMap中的newNode方法:

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<K,V>(hash, key, value, e);
        linkNodeLast(p);
        return p;
    }

  linkNodeLast方法:维护确保元素存储顺序的双向链表:

// link at the end of list
    private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail;
        tail = p;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
    }

  其中LinkedHashMap.Entry继承自HashMap.Node,在HashMap.Node基础上增加了前后两个指针域,注意,HashMap.Node中的next域也存在。

/**
     * HashMap.Node subclass for normal LinkedHashMap entries.
     */
    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }

  同理,newTreeNode也一样,调用的是linkedHashMap中的newTreeNode方法

TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
        TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
        linkNodeLast(p);
        return p;
    }

  说明2:注意位于第43、50行的afterNodeAccess和afterNodeInsertion,因为linkedHashMap中重写了这两个方法,所以实际中调动的是linkedHashMap中的这两个方法,和上述的newNode一样,也是多态的概念。

  afterNodeAccess:

void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMap.Entry<K,V> last;
        if (accessOrder && (last = tail) != e) {
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            p.after = null;
            if (b == null)
                head = a;
            else
                b.after = a;
            if (a != null)
                a.before = b;
            else
                last = b;
            if (last == null)
                head = p;
            else {
                p.before = last;
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }

  说明:此函数在很多函数(如put时key已经存在,get等)中都会被回调,若访问顺序为true,且访问的对象不是尾结点,则下面的图展示了访问前和访问后的状态,假设访问的结点为结点3

  说明:从图中可以看到,结点3链接到了双向链表原尾结点后面,变成了新的尾节点。

  afterNodeInsertion:

void afterNodeInsertion(boolean evict) { // possibly remove eldest
        LinkedHashMap.Entry<K,V> first;
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }

  removeEldestEntry:

 1 /**
 2      * Returns <tt>true</tt> if this map should remove its eldest entry.
 3      * This method is invoked by <tt>put</tt> and <tt>putAll</tt> after
 4      * inserting a new entry into the map.  It provides the implementor
 5      * with the opportunity to remove the eldest entry each time a new one
 6      * is added.  This is useful if the map represents a cache: it allows
 7      * the map to reduce memory consumption by deleting stale entries.
 8      *
 9      * <p>Sample use: this override will allow the map to grow up to 100
10      * entries and then delete the eldest entry each time a new entry is
11      * added, maintaining a steady state of 100 entries.
12      * <pre>
13      *     private static final int MAX_ENTRIES = 100;
14      *
15      *     protected boolean removeEldestEntry(Map.Entry eldest) {
16      *        return size() &gt; MAX_ENTRIES;
17      *     }
18      * </pre>
19      *
20      * <p>This method typically does not modify the map in any way,
21      * instead allowing the map to modify itself as directed by its
22      * return value.  It <i>is</i> permitted for this method to modify
23      * the map directly, but if it does so, it <i>must</i> return
24      * <tt>false</tt> (indicating that the map should not attempt any
25      * further modification).  The effects of returning <tt>true</tt>
26      * after modifying the map from within this method are unspecified.
27      *
28      * <p>This implementation merely returns <tt>false</tt> (so that this
29      * map acts like a normal map - the eldest element is never removed).
30      *
31      * @param    eldest The least recently inserted entry in the map, or if
32      *           this is an access-ordered map, the least recently accessed
33      *           entry.  This is the entry that will be removed it this
34      *           method returns <tt>true</tt>.  If the map was empty prior
35      *           to the <tt>put</tt> or <tt>putAll</tt> invocation resulting
36      *           in this invocation, this will be the entry that was just
37      *           inserted; in other words, if the map contains a single
38      *           entry, the eldest entry is also the newest.
39      * @return   <tt>true</tt> if the eldest entry should be removed
40      *           from the map; <tt>false</tt> if it should be retained.
41      */
42     protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
43         return false;
44     }

  说一下这个方法的使用:

  afterNodeInsertion方法会在使用put和putAll方法的时候被调用。removeEldestEntry()方法返回true会删除map中的eldest entry(accessOrder为false:least recently inserted entry,accessOrder为true:least recently accessed entry),返回false不做任何操作。

  该方法给其子类定义了一个功能:返回true,每次有元素被added的时候,都会remove the eldest entry。这在linkedHashMap被用作缓存的时候是有用的,可以减少内存的消耗,不会无限制的存储元素。

  注释中也给了一个linkedHashMap用作缓存的例子:linkedHashMap只存储100个元素,当超过100个元素的时候,就会进行remove。

  无特别定义的情况下,afterNodeAccess(accessOrder为true)和afterNodeInsertion(重写该方法,返回true值)都不会执行里面的操作。所以linkedHashMap中存储元素时和hashMap是一样的,只是多了一个维护元素顺序的双向链表。

  4.2 删:remove和removeNode函数----删除元素

  所使用的是hashMap中的remove和removeNode函数

/**
     * Removes the mapping for the specified key from this map if present.
     *
     * @param  key key whose mapping is to be removed from the map
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V remove(Object key) {
        Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }

  removeNode方法

 1 /**
 2      * Implements Map.remove and related methods
 3      *
 4      * @param hash hash for key
 5      * @param key the key
 6      * @param value the value to match if matchValue, else ignored
 7      * @param matchValue if true only remove if value is equal
 8      * @param movable if false do not move other nodes while removing
 9      * @return the node, or null if none
10      */
11     final Node<K,V> removeNode(int hash, Object key, Object value,
12                                boolean matchValue, boolean movable) {
13         Node<K,V>[] tab; Node<K,V> p; int n, index;
14         if ((tab = table) != null && (n = tab.length) > 0 &&
15             (p = tab[index = (n - 1) & hash]) != null) {
16             Node<K,V> node = null, e; K k; V v;
17             if (p.hash == hash &&
18                 ((k = p.key) == key || (key != null && key.equals(k))))
19                 node = p;
20             else if ((e = p.next) != null) {
21                 if (p instanceof TreeNode)
22                     node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
23                 else {
24                     do {
25                         if (e.hash == hash &&
26                             ((k = e.key) == key ||
27                              (key != null && key.equals(k)))) {
28                             node = e;
29                             break;
30                         }
31                         p = e;
32                     } while ((e = e.next) != null);
33                 }
34             }
35             if (node != null && (!matchValue || (v = node.value) == value ||
36                                  (value != null && value.equals(v)))) {
37                 if (node instanceof TreeNode)
38                     ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
39                 else if (node == p)
40                     tab[index] = node.next;
41                 else
42                     p.next = node.next;
43                 ++modCount;
44                 --size;
45                 afterNodeRemoval(node);
46                 return node;
47             }
48         }
49         return null;
50     }

  注意第45行的afterNodeRemoval方法,该方法在linkedHashMap中被重写,所以实际调用的是linkedHashMap中的afterNodeRemoval方法

void afterNodeRemoval(Node<K,V> e) { // unlink
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        p.before = p.after = null;
        if (b == null)
            head = a;
        else
            b.after = a;
        if (a == null)
            tail = b;
        else
            a.before = b;
    }

  说明:该方法就是重新维护双向链表中元素的前后联系。

  4.3 改:putVal函数----修改元素

  详见4.1,与添加元素是同一个操作。

  4.4 查:get和getNode方法----查找元素

  linkedHashMap中重写了get方法,但其中的getNod方法使用的还是hashMap中的getNode方法

/**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
     * key.equals(k))}, then this method returns {@code v}; otherwise
     * it returns {@code null}.  (There can be at most one such mapping.)
     *
     * <p>A return value of {@code null} does not <i>necessarily</i>
     * indicate that the map contains no mapping for the key; it's also
     * possible that the map explicitly maps the key to {@code null}.
     * The {@link #containsKey containsKey} operation may be used to
     * distinguish these two cases.
     */   
    public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)
            afterNodeAccess(e);
        return e.value;
    }

  与hashMap中的get方法不同的是,linkedHashMap中根据accessOrder的真假来调用afterNodeAccess方法,为true,get查询元素的时候就会重新进行双向链表的维护,将最近一次访问的元素置于双向链表的尾部,为false,不做下一步操作。参见4.1中的afterNodeAccess方法。

  举例(accessOrder为true):

public class Test {
    public static void main(String[] args) {
        LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap<>(16,0.75f,true);
        linkedHashMap.put("111","111");
        linkedHashMap.put("222","222");
        linkedHashMap.put("333","333");
        linkedHashMap.put("444","444");
        System.out.println("未进行访问之前=========" + linkedHashMap);
        linkedHashMap.get("222");
        System.out.println("进行访问之后===========" + linkedHashMap);
    }
}

  结果:

未进行访问之前========={111=111, 222=222, 333=333, 444=444}
进行访问之后==========={111=111, 333=333, 444=444, 222=222}

  4.5 containsValue函数----map是否存在该value

/**
     * Returns <tt>true</tt> if this map maps one or more keys to the
     * specified value.
     *
     * @param value value whose presence in this map is to be tested
     * @return <tt>true</tt> if this map maps one or more keys to the
     *         specified value
     */
    public boolean containsValue(Object value) {
        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
            V v = e.value;
            if (v == value || (value != null && value.equals(v)))
                return true;
        }
        return false;
    }

  说明:containsValue函数根据双链表结构来查找是否包含value,是按照插入顺序进行查找的,与HashMap中的此函数查找方式不同,HashMap是使用按照桶遍历,没有考虑插入顺序。

  hashMap中的containsValue函数

/**
     * Returns <tt>true</tt> if this map maps one or more keys to the
     * specified value.
     *
     * @param value value whose presence in this map is to be tested
     * @return <tt>true</tt> if this map maps one or more keys to the
     *         specified value
     */
    public boolean containsValue(Object value) {
        Node<K,V>[] tab; V v;
        if ((tab = table) != null && size > 0) {
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                    if ((v = e.value) == value ||
                        (value != null && value.equals(v)))
                        return true;
                }
            }
        }
        return false;
    }

 五、总结

  总得来看,LinkedHashMap的实现就是HashMap+LinkedList的实现方式,以HashMap维护数据结构,以LinkList的方式维护数据插入顺序。

  最后说一下利用linkedHashMap实现LRUCache(LRU算法缓存)

  LRUCache源码如下:

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

    private static final long serialVersionUID = 1L;
    private final int         maxSize;

    public LRUCache(int maxSize){
        this(maxSize, 16, 0.75f, false);
    }

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

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

  顾名思义,LRUCache就是基于LRU算法的Cache(缓存),这个类继承自LinkedHashMap,而类中看到没有什么特别的方法,这说明LRUCache实现缓存功能都是源自LinkedHashMap的。LinkedHashMap可以实现LRU算法的缓存基于两点:

  1、LinkedList首先它是一个Map,Map是基于K-V的,和缓存一致

  2、LinkedList提供了一个boolean值可以让用户指定是否实现LRU

  那么,首先我们了解一下什么是LRU:LRU即Least Recently Used,最近最少使用,也就是说,当缓存满了,会优先淘汰那些最近最不常访问的数据。比方说数据a,1天前访问了;数据b,2天前访问了,缓存满了,优先会淘汰数据b。

  上述linkedHashMap的构造函数中有个带布尔值的:

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

  这个accessOrder,它表示:

  (1)false,所有的Entry按照插入的顺序排列

  (2)true,所有的Entry按照访问的顺序排列

  第二点的意思就是,如果有1 2 3这3个Entry,那么访问了1,就把1移到尾部去,即2 3 1。每次访问都把访问的那个数据移到双向队列的尾部去,那么每次要淘汰数据的时候,双向队列最头的那个数据不就是最不常访问的那个数据了吗?换句话说,双向链表最头的那个数据就是要淘汰的数据。

  "访问",这个词有两层意思:

  1、根据Key拿到Value,也就是get方法

  2、修改Key对应的Value,也就是put方法

  根据linkedHashMap的源码也可以看到,在调用get方法和put方法的时候,都会调用afterNodeAccess方法,accessOrder为true就会重新进行元素的排序。

  至于如何淘汰掉最近最少使用的元素,当我们在linkedHashMap中增加元素(put)的时候,都会调用afterNodeInsertion函数,当我们重写afterNodeInsertion函数中的removeEldestEntry函数并返回true时,就会进行删除最近最少使用的元素了。

  举例说明:

public class LRULinkedHashMap {
    //定义缓存的容量
    private int cacheSize;

    private LinkedHashMap<String,String> cacheMap;

    public LRULinkedHashMap(int cacheSize){
        this.cacheSize = cacheSize;

        cacheMap = new LinkedHashMap(16, 0.75F, true){
            //重写linkedHashMap中的removeEldestEntry方法,
            @Override
            protected boolean removeEldestEntry(Map.Entry eldest) {
                //当linkedHashMap中元素的个数大于缓存的个数时,返回true,删除最近最少使用的元素
                if(cacheMap.size() > cacheSize){
                    return true;
                }else{
                    return false;
                }
            }
        };
    }

    public void put(String key, String value){
        cacheMap.put(key, value);
    }

    public String get(String key){
        return cacheMap.get(key);
    }

    public Set<Map.Entry<String, String>> getAll(){
        return cacheMap.entrySet();
    }

    public static void main(String[] args) {
        LRULinkedHashMap map = new LRULinkedHashMap(3);
        map.put("key1", "1");
        map.put("key2", "2");
        map.put("key3", "3");

        for (Map.Entry<String, String> e : map.getAll()){
            System.out.println(e.getKey()+"====>"+e.getValue());
        }
        System.out.println("\n");
        map.get("key2");
        for (Map.Entry<String, String> e : map.getAll()){
            System.out.println(e.getKey()+"====>"+e.getValue());
        }
        System.out.println("\n");
        map.put("key4", "4");
        for (Map.Entry<String, String> e : map.getAll()){
            System.out.println(e.getKey()+"====>"+e.getValue());
        }

    }
}

  结果:

key1====>1
key2====>2
key3====>3


key1====>1
key3====>3
key2====>2


key3====>3
key2====>2
key4====>4

  说明:自定义一个基于linkedHashMap实现的缓存类,缓存容量为3,当添加了三个元素后进行get访问,再put第四个元素,根据结果来看,符合上面的分析。

参考:

https://www.cnblogs.com/xrq730/p/5052323.html

https://www.cnblogs.com/leesf456/p/5248868.html

posted @ 2019-02-23 14:36  吹灭读书灯  阅读(653)  评论(0编辑  收藏  举报