RUL算法实现

493 阅读2分钟

1.双向列表实现RUL

public class LRUCache {
    LinkedList<Node> cache;
    int capacity;

    public LRUCache(LinkedList<Node> cache, int capacity) {
        this.cache = cache;
        this.capacity = capacity;
    }

    public int  get(int key){
        int result = -1;
        //队列的逆向迭代器
        Iterator<Node> iterator = cache.descendingIterator();
        while (iterator.hasNext()){
            Node node = iterator.next();
            if(node.key == key){
                result = node.val;
                iterator.remove();
                //添加到列表尾部
                put(key,node.val);
                break;
            }
        }
        return result;
    }


    public void put(int key , int value){
        Iterator<Node> iterator = cache.iterator();
        while (iterator.hasNext()){
            Node node = iterator.next();
            if(node.key == key){
                iterator.remove();
                break;
            }
        }

        if(capacity == cache.size()){
            //缓存已经满了 删除最近最少访问的 一个元素(表头)
            cache.removeFirst();
        }

        cache.add(new Node(key,value));
    }

    class Node {
        int key;
        int val;

        public Node(int key, int val) {
            this.key = key;
            this.val = val;
        }
    }

    public static void main(String[] args) {
        LRUCache cache  = new LRUCache(new LinkedList<>(),10);
        for(int i = 0 ; i < 10 ; i++){
            cache.put(i,i);
        }

        cache.get(1);
        cache.get(3);
        cache.put(12,12);
        System.out.println(cache.get(3));
    }
}

2.使用LinkedHashMap实现

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Set;

/**
 *
 * LinkedHashMap 实现
 * put /get 操作 O(1)
 * 特殊情况:缓存已满,需要删除链表头
 * @author FYG
 * @date 2020/10/28
 * @description
 */

public class LRUCache2 {

    LinkedHashMap<Integer,Integer> cache;

    int capacity ;

    public LRUCache2(int capacity) {
        this.cache = new LinkedHashMap<>(capacity);
        this.capacity = capacity;
    }

    public int get(int key){
        if(!cache.containsKey(key)){
            return -1;
        }

        Integer val = cache.get(key);
        //从链表中删除
        cache.remove(key);
        //添加到链尾
        cache.put(key,val);
        return val;
    }

    public void put(int key,int value){
        if(cache.containsKey(key)){
            cache.remove(key);
        }

        if(cache.size() == capacity){
            //cache已经满了  删除表头
            Set<Integer> keySet = cache.keySet();
            Iterator<Integer> iterator = keySet.iterator();
            cache.remove(iterator.hasNext());
        }

        //添加到链尾
        cache.put(key,value);

    }


}

3.LinkedHashMap 本身内部有一个触发条件则自动执行的方法:删除最老元素(最近最少使用的元素)

/**
 * LinkedHashMap 本身内部有一个触发条件则自动执行的方法:删除最老元素(最近最少使用的元素)
 * 由于最近最少使用元素是 LinkedHashMap 内部处理
 * 故我们不再需要维护 最近访问元素放在链尾,get 时直接访问/ put 时直接存储
 *
 * @author FYG
 * @date 2020/10/28
 * @description
 */

public class LRUCache3 {
    private Map<Integer, Integer> map;

    private int capacity;

    public LRUCache3(int capacity) {
        this.capacity = capacity;
        this.map = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
                return size() > capacity; // 容量大于capacity 时就删除
            }
        };
    }

    public int get(int key) {
        return map.getOrDefault(key, -1);
    }

    public void put(int key, int val) {
        map.put(key, val);
    }
}

最后一种算法我们从源码看一下

HashMap.java


final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
       ...
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e); // 这里是把当前的节点放到对尾部
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict) ; // 这里移除最老的元素
        return null;
    }

LinkedHashMap.java

 
 void afterNodeInsertion(boolean evict) { // possibly remove eldest
        LinkedHashMap.Entry<K,V> first;
        //我们重写的removeEldestEntry 判断是否删除最老的元素
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }
    
 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;
        }
    }