LRU算法以及其实现

喜夏-厌秋 提交于 2020-01-26 02:54:29

LRU算法

LRU算法是什么

  • 最近最久未使用

LRU的实现

利用双向链表实现

双向链表的特点就是他的链表是双路的,定义好头结点和尾节点后,利用先进先出(FIFO),最近被放入的数据会最早被获取

  • java实现

    • 定义基本的链表操作节点

      public class Node {
          //键
          Object key;
          //值
          Object value;
          //上一个节点
          Node pre;
          //下一个节点
          Node next;
      
          public Node(Object key, Object value) {
              this.key = key;
              this.value = value;
          }
      }
      
    • 链表基本定义

      • 定义一个LRU类,然后定义它的大小,容量,头节点,尾节点等部分,然后定义一个基本的构造方法
      public class LRU<K, V> {
          //当前大小
          private int currentSize;
          //总容量
          private int capcity;
          //所有的node节点
          private HashMap<K,Node> caches;
          //头结点
          private Node first;
          //尾节点
          private  Node last;
      
          public LRU(int size){
              currentSize=0;
              this.capcity=size;
              caches=new HashMap<K,Node>(size);
          }
      
    • 添加元素

      • 首先先判断是否已经存在,如果是新元素,判断当前大小是否大于等于总容量,防止因为添加导致超出表大小,最后将添加的节点移动到头部。
       public void put(K key,V value){
              //先看看里面是否存在
              Node node =caches.get(key);
              //如果是新元素
              if(node==null){
                  //判断是否超过最大容量
                  if(caches.size()>=capcity){
                      //将最后一个节点删除
                      caches.remove(last.key);
                      removeLast();
                  }
                  //创建新节点
                  node=new Node(key,value);
              }
      
              //如果存在就覆盖旧值
              node.value=value;
              //把元素移到首部
              moveToHead(node);
              caches.put(key,node);
          }
      
    • 访问元素

      • 先判断是否存在,存在的话,先把数据移到头部节点,然后返回旧值。
       public Object get (K key){
              Node node = caches.get(key);
              if(node==null){
                  return null;
              }else {
                  //把访问节点移到首节点
                  moveToHead(node);
                  return node;
              }
          }
      
    • 节点删除操作

      • 在删除的时候需要注意的是,把前后节点的指针给进行相应的更改
       public Object remove(K key){
              Node node= caches.get(key);
              if(node != null){
                  if(node.pre!=null){
                      node.pre.next=node.next;
                  }
                  if (node.next!=null){
                      node.next.pre=node.pre;
                  }
                  if(node==first){
                      first=node.next;
                  }
                  if(node==last){
                      last=node.pre;
                  }
              }
              return caches.remove(key);
          }
      
    • 移动元素到头节点

      • 首先把当前节点移除(类似于删除),然后将其放到首节点
       private void moveToHead(Node node) {
              if (first == node) {
                  return;
              }
              if (node.next != null) {
                  node.next.pre = node.pre;
              }
              if (node.pre != null) {
                  node.pre.next = node.next;
              }
              if (node == last) {
                  last = last.pre;
              }
              if (first == null || last == null) {
                  first = last = node;
                  return;
              }
              node.next = first;
              first.pre = node;
              first = node;
              first.pre = null;
          }
      
  • LRU完成代码

    import java.util.HashMap;
    
    public class LRU<K, V> {
        //当前大小
        private int currentSize;
        //总容量
        private int capcity;
        //所有的node节点
        private HashMap<K,Node> caches;
        //头结点
        private Node first;
        //尾节点
        private  Node last;
    
        public LRU(int size){
            currentSize=0;
            this.capcity=size;
            caches=new HashMap<K,Node>(size);
        }
        /**
         * @Author: sun mingzhi
         * @Description: 添加元素
         * @Date: 2020/1/19 10:06
    
         * @Return: [key, value]
         */
        public void put(K key,V value){
            //先看看里面是否存在
            Node node =caches.get(key);
            //如果是新元素
            if(node==null){
                //判断是否超过最大容量
                if(caches.size()>=capcity){
                    //将最后一个节点删除
                    caches.remove(last.key);
                    removeLast();
                }
                //创建新节点
                node=new Node(key,value);
            }
    
            //如果存在就覆盖旧值
            node.value=value;
            //把元素移到首部
            moveToHead(node);
            caches.put(key,node);
        }
        /**
         * @Author: sun mingzhi
         * @Description: 通过key获取元素
         * @Date: 2020/1/19 10:40
    
         * @Return: 
         */
        public Object get (K key){
            Node node = caches.get(key);
            if(node==null){
                return null;
            }else {
                //把访问节点移到首节点
                moveToHead(node);
                return node;
            }
        }
        /**
         * @Author: sun mingzhi
         * @Description: 通过key删除元素
         * @Date: 2020/1/19 10:42
    
         * @Return: 
         */
        public Object remove(K key){
            Node node= caches.get(key);
            if(node != null){
                if(node.pre!=null){
                    node.pre.next=node.next;
                }
                if (node.next!=null){
                    node.next.pre=node.pre;
                }
                if(node==first){
                    first=node.next;
                }
                if(node==last){
                    last=node.pre;
                }
            }
            return caches.remove(key);
        }
        /**
         * 清除所有节点
         */
        public void clear() {
            first = null;
            last = null;
            caches.clear();
        }
    
        /**
         * 把当前节点移动到首部
         * @param node
         */
        private void moveToHead(Node node) {
            if (first == node) {
                return;
            }
            if (node.next != null) {
                node.next.pre = node.pre;
            }
            if (node.pre != null) {
                node.pre.next = node.next;
            }
            if (node == last) {
                last = last.pre;
            }
            if (first == null || last == null) {
                first = last = node;
                return;
            }
            node.next = first;
            first.pre = node;
            first = node;
            first.pre = null;
        }
    
        /**
         * 移除最后一个节点
         */
        private void removeLast() {
            if (last != null) {
                last = last.pre;
                if (last == null) {
                    first = null;
                } else {
                    last.next = 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) {
            LRU<Integer, String> lru = new LRU<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());
    
            lru.get(4);
            System.out.println("获取key为4的元素之后的链表:"+lru.toString());
    
            lru.put(6,"f");
            System.out.println("新添加一个key为6之后的链表:"+lru.toString());
    
            lru.remove(3);
            System.out.println("移除key=3的之后的链表:"+lru.toString());
        }
    }
    

利用LinkedHashMap实现

​ LinkedHashMap底层就是使用HashMap加双链表实现的,而且本身已经实现了按照访问顺序存储,此外LinkedHashMap本身就实现了一个removeEldestEnter用于判断是否需要移除最不常读取的数,方法默认返回false,不会移除元素,只需要重写此方法即可。

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

public class LRU<K, V> {
    private static final float hashLoadFactory = 0.75f;
    private LinkedHashMap<K,V> map;
    private int cacheSize;

    public LRU(int cacheSize) {
        this.cacheSize = cacheSize;
        int capacity = (int)Math.ceil(cacheSize / hashLoadFactory) + 1;
        //true 表示让LinkedHashMap按照访问顺序来进行排序,最近访问的放在头部,最后访问的放在尾部
        map = new LinkedHashMap<K,V>(capacity, hashLoadFactory, true){
            private static final long serialVersionUID = 1;
            @Override
            protected boolean removeEldestEntry(Map.Entry eldest) {
                //当map中的数据量大于某个指定的缓存个数的时候,就自动删除最老的元素
                return size() > LRU.this.cacheSize;
            }
        };
    }

    public synchronized V get(K key) {
        return map.get(key);
    }

    public synchronized void put(K key, V value) {
        map.put(key, value);
    }

    public synchronized void clear() {
        map.clear();
    }

    public synchronized int usedSize() {
        return map.size();
    }

    public void print() {
        for (Map.Entry<K, V> entry : map.entrySet()) {
            System.out.print(entry.getValue() + "--");
        }
        System.out.println();
    }
    public static void main(String[] args) {
        LRU<Integer, String> lru = new LRU<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.print();
        System.out.println("获取key为4的元素之后的链表:");
        lru.get(4);
        lru.print();
        System.out.println("新添加一个key为6之后的链表:");
        lru.put(6,"f");
        lru.print();
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!