java实现LRU(Least Recently Used)最近最少使用的

之前公司在消费MQ的时候,保存了最近消费100条消息的ID,用于去重,想着应该是这种思想实现的,java LinkedHashMap有自带的这种功能,今天试着自己写一个,借助HashMap来实现。

做个笔记,学习学习

1.LinkedHashMap

我这儿说的比较简单,要深入理解的话,还得熟悉数据结构和查看源码!

public static void main(String[] args)  {
    /**
     * LinkedHashMap继承了HashMap,LinkedHashMap内部维护了一个链表
     * 我们向LinkedHashMap添加、删除、查询一个KV时
     * 其实调用的是HashMap的put、remove、get 方法
     * 通过源码HashMap源码可以看到,调用这些方法后
     * 会调用对应的方法,在HashMap里面是空方法体
     *     void afterNodeAccess(Node<K,V> p) { }
     *     void afterNodeInsertion(boolean evict) { }
     *     void afterNodeRemoval(Node<K,V> p) { }
     * 这几个就是在LinkedHashMap里面实现了的方法
     * 比如LinkedHashMap源码:
     * 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);
     *     }
     * }
     * 可以看到if里面有一个条件removeEldestEntry(first) 需要为true,就会remove一个head(first)
     * 但是LinkedHashMap里面这个方法默认为false,所以可以在构造器里面重新这个方法
     * 在插入一个节点后,当满足我们指定的条件时,就会把链表的head(first)节点删除掉,实现LRU
     */
    int cacheSize = 100;
    Map<String, String> map = new LinkedHashMap<String, String>
            (cacheSize, (float) 0.75, true){
        @Override
        protected boolean removeEldestEntry(Map.Entry eldest) {
            //满足这个条件时,就会删除第一个节点啦
            return size()>cacheSize;
        }
    };
    //LruLinkedMap<String, String> map = new LruLinkedMap<>();
    map.put("a","aa");
}

2.自已借助HashMap 简单实现一个

/**
 * 来个自己的Node节点接口,也可以实现Map.Entry<K,V>
 * 但是用Map.Entry,debug的时候,可能看不到想看的一些信息?
 */
public interface MyNode <K,V>{
    K getKey();
    V getValue();
}
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class LruLinkedMap<K,V> implements Iterable<MyNode<K,V>>{

    private final Map<K,Node<K,V>> hashMap;
    private int size;
    private Node<K,V> head, tail;
    private static final int DEFAULT_CAPACITY = 1 << 4; //16

    public LruLinkedMap() {
        this(DEFAULT_CAPACITY);
    }
    public LruLinkedMap(int capacity) {
        this.hashMap = new HashMap<>(capacity);
        this.size = 0;
        this.head = this.tail = null;
    }

    public int size(){
        return size;
    }

    /**
     * 获取map里面的Node里面的value
     * @param key key 不为空
     * @return value
     */
    public V get(K key){
        Node<K,V> node = hashMap.get(key);
        if(node != null){
            afterNodeAccess(node);
            return node.value;
        }
        return null;
    }

    /**
     * 把node放入map,如果key存在就更新node,如果不存在就新建一个node
     * @param key key
     * @param  value value
     * @return 返回上次存在的value
     */
    public V put(K key, V value){
        //如果已经存在该key
        Node<K,V> node = hashMap.get(key);
        if(node != null){
            node.value = value;
            node = hashMap.put(key, node);
            afterNodeAccess(node);
            return node == null ? null : node.value;
        }

        //如果不存在key
        node = newNode(key, value, tail);
        hashMap.put(key, node);
        size = hashMap.size();
        afterNodeInsert(node);
        return null;
    }

    /**
     *
     * @param key key
     * @return node
     */
    public Node<K,V> remove(K key){
        //如果存在
        Node<K,V> node = hashMap.remove(key);
        if(node != null){
            size = hashMap.size();
            afterNodeRemove(node);
        }
        //如果不存在
        return node;
    }

    /**
     * 操作节点后,把节点移动到最后
     * @param node 当前节点
     */
    private void afterNodeAccess(Node<K,V> node) {
        //如果node为头节点
        if(head == node){
            //如果head.nextNode == null 说明 只有一个节点(头结点)
            if(head.nextNode != null){
                head = head.nextNode;
                head.preNode = null;
                tail.nextNode = node;
                node.preNode = tail;
                tail = node;
            }
        }else if(tail != node){
            //如果node为尾节点,就不用移动节点了
            Node<K,V> pre = node.preNode;
            Node<K,V> next = node.nextNode;
            //...pre(p,node) node(pre,next) next(node,n4)...tail(x,null)
            pre.nextNode = next;
            //...pre(p,node->next) node(pre,next) next(node,n4)...tail(x,null)
            next.preNode = pre;
            //...pre(p,next) node(pre,next) next(node->pre,n4)...tail(x,null)
            node.nextNode = null;
            //...pre(p,next) node(pre,next->null) next(node->pre,n4)...tail(x,null)
            node.preNode = tail;
            //...pre(p,next) node(pre->tail,null) next(node->pre,n4)...tail(x,null)
            tail.nextNode = node;
            //...pre(p,next) node(tail,null) next(pre,n4)...tail(x,null->node)
            tail = node;
            //...pre(p,next)  next(pre,n4)...tail(x,node) node(tail,null)
        }
    }

    /**
     * 有新的节点插入到map时,更新尾巴tail
     * @param newNode 新节点
     */
    private void afterNodeInsert(Node<K,V> newNode) {
        if(head == null){
            head = newNode;
            tail = head;
        }else {
            tail.nextNode = newNode;
            tail = newNode;
        }
    }

    /**
     *
     * @param node 当前节点
     */
    private void afterNodeRemove(Node<K, V> node) {
        //如果是头结点或者seize()==1 两种情况
        if(head == node){
            if((head = head.nextNode) != null){
                head.preNode = null;
            }
        }else if(tail == node){
            //不存在tail.preNode == null
            tail = tail.preNode;
            tail.nextNode = null;
        }else{
            Node<K,V> pre = node.preNode;
            Node<K,V> next = node.nextNode;
            pre.nextNode = next;
            next.preNode = pre;
        }
    }

    //返回一个新的Node节点
    private Node<K,V> newNode(K key, V value, Node<K,V> preNode){
        return new Node<>(key, value, preNode, null);
    }

    @Override
    public Iterator<MyNode<K, V>> iterator() {
        return new NodeIterator();
    }

    final class NodeIterator implements Iterator<MyNode<K,V>>{

        private Node<K,V> node;

        private NodeIterator() {
            this.node = head;
        }

        @Override
        public boolean hasNext() {
            return node != null;
        }

        @Override
        public MyNode<K, V> next() {
            Node<K,V> n = node;
            node = node.nextNode;
            return n;
        }
    }

    /**
     * implements {@link Map.Entry}, base node
     * @param <K>
     * @param <V>
     */
    static class Node<K,V> implements MyNode<K,V>{
        final K key;
        V value;
        Node<K,V> preNode;
        Node<K,V> nextNode;

        Node(K key, V value, Node<K, V> preNode, Node<K, V> nextNode) {
            this.key = key;
            this.value = value;
            this.preNode = preNode;
            this.nextNode = nextNode;
        }

        public K getKey() {
            return key;
        }
        public V getValue() {
            return value;
        }
        public V setValue(V newValue) {
            V oldValue = this.value;
            this.value = newValue;
            return oldValue;
        }
    }   
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值