集合之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()返回此映射中的鍵值映射數
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章