LRU算法

LRU

        Least Recently Used    最近最久未使用。缓存算法。

        com.mysql.jdbc.util.LRUCache   最简化版的继承LinkedHashMap<Object, Object>
        org.apache.dubbo.common.utils.LRUCache   继承LinkedHashMap,加了一个成员变量Lock可重入锁,所有的增删改查清都显示lock()/unlock()

借助LinkedHashMap实现:

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

public class LRUCache2 extends LinkedHashMap {

    private int maxSize;

    public LRUCache2(int size) {
        // int initialCapacity, float loadFactor, boolean accessOrder
        super(size, 0.75f, true);
        maxSize = size;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        // 重写removeEldestEntry方法:是否需要删除队尾元素。putVal等插入方法会在底部调用这个方法做检查。LinkedHashMap里面是直接return false,所以需要重写
        return size() > maxSize;
    }

    public static void main(String[] args) {
        LRUCache2 lru = new LRUCache2(5);
        lru.put(1, "a");
        lru.put(2, "b");
        lru.put(3, "c");
        lru.put(4, "d");
        lru.put(5, "e");
        System.out.println("原始链表为:" + lru.toString());    //  原始链表为:{1=a, 2=b, 3=c, 4=d, 5=e}

        lru.get(4);
        System.out.println("获取key为4的元素之后的链表:" + lru.toString());  //  获取key为4的元素之后的链表:{1=a, 2=b, 3=c, 5=e, 4=d}

        lru.put(6, "f");
        System.out.println("新添加一个key为6之后的链表:" + lru.toString());  //  新添加一个key为6之后的链表:{2=b, 3=c, 5=e, 4=d, 6=f}

        lru.remove(3);
        System.out.println("移除key=3的之后的链表:" + lru.toString());       //  移除key=3的之后的链表:{2=b, 5=e, 4=d, 6=f}
    }

}

 

利用链表和hashmap实现:

           新增:如果新数据项在链表中存在,则把该节点移到链表头部;如果不存在,则新建一个节点放到链表头部;若缓存满了,则把链表最后一个节点删除。
           查找:如果数据项在链表中存在,则把该节点移到链表头部并返回对应节点,否则返回-1。

   在链表尾部的节点就是最近最久未访问的数据项,链表头节点是最活跃的数据项。

import java.util.concurrent.ConcurrentHashMap;

public class LRUCacheMy<K, V> {

    private int maxSize;
    private ConcurrentHashMap<K, Node> map;
    private Node first, last;

    class Node {
        K key;
        V value;
        Node pre, next;
        Node(K k, V v) {
            this.key = k;
            this.value = v;
        }
    }

    public LRUCacheMy(int size) {
        this.maxSize = size;
        map = new ConcurrentHashMap<>(size);    //初始化map大小
    }


    public void put(K k, V v) {
        Node n = map.get(k);
        if (n != null) {
            n.value = v;
        } else {
            n = new Node(k, v);
            ensureCapacity();
        }
        // 把node挪到链表头、更新map
        moveNodeToHead(n);
        map.put(k, n);
    }

    public Object get(K k) {
        Node n = map.get(k);
        if (n == null) {
            return null;
        }
        // 把node挪到链表头
        moveNodeToHead(n);
        return n.value;
    }

    public Object remove(K k) {
        Node n = map.get(k);
        if (n == null) {
            return null;
        }
        // 删链表的元素、删map的元素
        removeNode(n);
        return map.remove(k);
    }


    /** 删除链表中的节点:改上下节点指向、判断首尾 */
    private void removeNode(Node n) {
        if (n.next != null) {
            n.next.pre = n.pre;
        }
        if (n.pre != null) {
            n.pre.next = n.next;
        }
        if (n == last) {
            last = n.pre;
        }
        if (n == first) {
            first = n.next;
        }
        n.pre = null;
        n.next = null;
    }
    /** 把节点移到头部:先判首再判null和尾,再改上下节点指向、挂到头部 */
    private void moveNodeToHead(Node n) {
        if (n == first) {
            return;
        }
        if (first == null || last == null) {
            first = last = n;
            return;
        }

        if (n == last) {
            last = n.pre;
        }
        if (n.pre != null) {
            n.pre.next = n.next;
        }
        if (n.next != null) {
            n.next.pre = n.pre;
        }

        n.pre = null;
        n.next = first;
        first.pre = n;
        first = n;
    }
    /** 确保容量够 */
    private void ensureCapacity() {
        if (map.size() < maxSize) {
            return;
        }
        // 删队尾元素
        if (last != null) {
            // 必须先删map
            map.remove(last.key);
            // 在删链表
            last = last.pre;
            if (last != null) {
                // 断开原last的关联
                last.next.pre = null;    
                last.next = null;
            } else {
                first = null;
            }
        }
    }



    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        Node node = first;
        while (node != null) {
            sb.append(String.format("%s:%s ", node.key, node.value));
            node = node.next;
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        LRUCacheMy<Integer, String> lru = new LRUCacheMy<Integer, String>(5);
        lru.put(1, "a");
        lru.put(2, "b");
        lru.put(3, "c");
        lru.put(4, "d");
        lru.put(5, "e");
        System.out.println("原始链表为:" + lru.toString());    //  原始链表为:5:e 4:d 3:c 2:b 1:a

        lru.get(4);
        System.out.println("获取key为4的元素之后的链表:" + lru.toString());  //  获取key为4的元素之后的链表:4:d 5:e 3:c 2:b 1:a

        lru.put(6, "f");
        System.out.println("新添加一个key为6之后的链表:" + lru.toString());  //  新添加一个key为6之后的链表:6:f 4:d 5:e 3:c 2:b

        lru.remove(3);
        System.out.println("移除key=3的之后的链表:" + lru.toString());      //  移除key=3的之后的链表:6:f 4:d 5:e 2:b
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值