• Java实现LRU算法


    一。LRU算法简介

    LRU(Least Recently Used)最近最久未使用算法

    常见应用场景:内存管理中的页面置换算法、缓存淘汰中的淘汰策略等

    二。实现理论

      底层结构:双向链表 + HashMap ,双向链表由特定的哈希节点组成。

    (1)访问节点时,将其从原来位置删除,插入到双向链表头部;
    (2)更新节点时,先删除原有缓存数据(即原有节点),然后更新map映射,再将更新值作为节点插入链表头;更新后,判断容量是否超过最大内存使用量
    (3)超过则执行淘汰;淘汰即删除双向链表最后一个节点,同时删除map中的映射
    (4)LRU实现中有频繁的查找节点并删除,为节省时间(链表查找目标节点需要遍历),使用HashMap保存键-节点映射关系,O(1)的查找+O(1)的删除
    (5)LRU实现中,要频繁的在头部插入,以及在尾部删除;因此,需要定义head、tail两个节点,方便操作
     
    三。代码
      1 package com.xl.Base;
      2 
      3 import java.util.HashMap;
      4 import java.util.Iterator;
      5 
      6 /**
      7  *    最近最久未使用淘汰策略
      8  *    基于 双向链表 + 哈希表组成,其中双向链表由哈希链表节点构成
      9  *    封装为 LRU(K, V)
     10  *    对外提供 get(K)访问数据、put(K, V)更新数据、Iterator()遍历数据
     11  */
     12 public class LRU<K, V> implements Iterable<K>{
     13     
     14     private Node head;
     15     private Node tail;
     16     //记录K-Node映射,便于快速查找目标数据对应节点
     17     private HashMap<K, Node> map;
     18     private int maxSize;
     19     
     20     //哈希链表节点类 Node
     21     private class Node{
     22         Node pre;
     23         Node next;
     24         K k;
     25         V v;
     26         
     27         //Node对外提供构造方法
     28         public Node(K k, V v) {
     29             this.k = k;
     30             this.v = v;
     31         }
     32     }
     33     
     34     //初始化时必须传入最大可用内存容量
     35     public LRU(int maxSize){
     36         this.maxSize = maxSize;
     37         //HashMap初始容量设置为 maxSize * 4/3,即达到最大可用内存时,HashMap也不会自动扩容浪费空间
     38         this.map = new HashMap<>(maxSize * 4 / 3);
     39         
     40         head.next = tail;
     41         tail.pre = head;
     42     }
     43 
     44     //获取指定数据
     45     private V get(K key) {
     46         //判断是否存在对应数据
     47         if(!map.containsKey(key)) {
     48             return null;
     49         }
     50         
     51         //最新访问的数据移动到链表头
     52         Node node = map.get(key);
     53         remove(node);
     54         addFirst(node);
     55         return node.v;
     56     }
     57     
     58     //更新旧数据或添加数据
     59     private void put(K key, V value) {
     60         //若存在旧数据则删除
     61         if(map.containsKey(key)) {
     62             Node node = map.get(key);
     63             remove(node);
     64         }
     65         
     66         //新数据对应节点插入链表头
     67         Node node = new Node(key, value);
     68         map.put(key, node);
     69         addFirst(node);
     70         
     71         //判断是否需要淘汰数据
     72         if(map.size() > maxSize) {
     73             removeLast();
     74             //数据节点淘汰后,同时删除map中的映射
     75             map.remove(node.k);
     76         }
     77     }
     78     
     79     //将指定节点插入链表头
     80     private void addFirst(Node node) {
     81         Node next = head.next;
     82         
     83         head.next = node;
     84         node.pre = head;
     85         
     86         node.next = next;
     87         next.pre = node;
     88     }
     89     
     90     //从链表中删除指定节点
     91     private void remove(Node node) {
     92         Node pre = node.pre;
     93         Node next = node.next;
     94         
     95         pre.next = next;
     96         next.pre = pre;
     97         
     98         node.next = null;
     99         node.pre = null;
    100     }
    101     
    102     //淘汰数据
    103     private Node removeLast() {
    104         //找到最近最久未使用的数据所对应节点
    105         Node node = tail.pre;
    106         
    107         //淘汰该节点
    108         remove(node);
    109         
    110         return node;
    111     }
    112     
    113     //通过迭代器遍历所有数据对应键
    114     @Override
    115     public Iterator<K> iterator() {
    116         return new Iterator<K>() {
    117             
    118             private Node cur = head.next;
    119 
    120             @Override
    121             public boolean hasNext() {
    122                 return cur != tail;
    123             }
    124 
    125             @Override
    126             public K next() {
    127                 Node node = cur;
    128                 cur = cur.next;
    129                 return node.k;
    130             }
    131             
    132         };
    133     }
    134     
    135 }
  • 相关阅读:
    UISegmentControl
    UISegmentControl,UISwich,UIPageControl
    UIPageControl,UISlider
    UISlider
    触摸事件,响应者链和手势
    NIO Socket
    处理多线程问题的工具类
    Java多线程疑难点
    软中断和硬中断的联系与区别
    进程—内存描述符(mm_struct)
  • 原文地址:https://www.cnblogs.com/xiang9286/p/11191568.html
Copyright © 2020-2023  润新知