集合之LinkedHashMap并且实现LRU

package com.huawei.test.jdk8.collectiondemo;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author 无极之岚
 * @date 2022/2/26 1:02 PM
 */
public class LinkedHashMapTest {
    public static void main(String[] args) {
        LinkedHashMap<Integer,String> lhm1 = new LinkedHashMap<Integer,String>();
        lhm1.put(1,"1");
        lhm1.put(2,"2");
        lhm1.put(3,"3");
        System.out.println("普通方法:");
        for (Map.Entry entry:lhm1.entrySet()){
            System.out.println(entry.getValue());
        }


        LinkedHashMap<Integer,String> lhm2 = new LinkedHashMap<Integer,String>(16,0.75f,true);
        lhm2.put(1,"1");
        lhm2.put(2,"2");
        lhm2.put(3,"3");
        System.out.println("access为true方法:");
        lhm2.get(2);
        for (Map.Entry entry:lhm2.entrySet()){
            System.out.println(entry.getValue());
        }
    }
}

世界之大无奇不有,LinkedHashMap 的构造函数放入不同的值,get方法会改变顺序,并且在打印时可以不按照插入顺序。

源码

/**
 * 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 
 ture就是访问顺序,false是插入顺序
 * @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;
}
// 空构造函数默认为false
public LinkedHashMap() {
    super();
    accessOrder = false;
}

accessOrder影响get函数的实现

public V get(Object key) {
    Node<K,V> e;
    if ((e = getNode(hash(key), key)) == null)
        return null;
    // accessOrder为true时将获取到的节点移动到尾部
    if (accessOrder)
        afterNodeAccess(e);
    return e.value;
}

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;
    }
}

LRU

Least Recently Used,最少使用,像redis等缓存中间件都有类似的算法,用来过期数据。而LinkedHashMap刚好有个方法removeEldestEntry,当方法返回true时,会将最不常用的节点移除。

/*
* 源码注解中提供了案例,设置最大的长度,超过则返回true,当执行put方法时都会去判断removeEldestEntry是否为true,就执行removeNode方法
<pre>
     *     private static final int MAX_ENTRIES = 100;
     *
     *     protected boolean removeEldestEntry(Map.Entry eldest) {
     *        return size() &gt; MAX_ENTRIES;
     *     }
     * </pre>
	 /
  protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
    return false;
}

实现如下:又上方案例可知,按访问顺序来排,每次get请求都会将数据放队尾,那么只要超过容量时,把队头的节点移除即可。

// 设置容量为10
    private static LRUCache<String, Integer> cache = new LRUCache<>(10);

    public static void main(String[] args) {
        //存入10个数据,之后cache满了
        for (int i = 0; i < 10; i++) {
            cache.put("Test" + i, i);   // 键值对
        }
        System.out.println("目前所有缓存:" + cache);
        cache.get("Test3");
        System.out.println("拿了Test3后的缓存:" + cache);
        cache.get("Test4");
        System.out.println("拿了Test4后的缓存:" + cache);
        cache.get("Test5");
        System.out.println("拿了Test5后的缓存:" + cache);
        cache.get("Test5");
        System.out.println("再拿了Test5后的缓存:" + cache);

        cache.put("Test" + 10, 10);  //cache已经满了,则会将最长没用的删除
        System.out.println("添加新元素10后的缓存(此时缓存已满):" + cache);
    }

    private static class LRUCache<K, V> extends LinkedHashMap<K, V> {
        private int cacheSize;

        //使用指定的初始容量、装载因子和排序模式构造一个空的LinkedHashMap实例
        //true表示访问顺序,false表示插入顺序
        public LRUCache(int cacheSize) {
            super(16, (float) 0.75, true);
            this.cacheSize = cacheSize;
        }

        //如果不重写这个 会导致在新put数据时,不会删除最久没使用的,而是会新增到后边
        @Override
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            //如果添加数据后,size()大于所要求的容量,则返回true,代表进行LRU删除
            return size() > cacheSize;  //size()返回此映射中的键值映射数
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章