【问题标题】:Best way to implement LRU cache实现 LRU 缓存的最佳方法
【发布时间】:2011-09-17 22:33:28
【问题描述】:

我正在研究 LRU 缓存实现的这个问题,在缓存的大小已满后,弹出最近最少使用的项目并被新项目替换。

我想到了两种实现方式:

1)。创建两个看起来像这样的地图

std::map<timestamp, k> time_to_key
std::map<key, std::pair<timestamp, V>> LRUCache

要插入一个新元素,我们可以将当前时间戳和值放入LRUCache。而当缓存的大小已满时,我们可以通过找到 time_to_key 中存在的最小时间戳并从 中删除相应的键来驱逐最近的元素LRUCache。 插入一个新item是O(1),更新时间戳是O(n)(因为我们需要在time_to_中查找时间戳对应的k

2)。有一个链表,其中最近最少使用的缓存出现在头部,新项目添加在尾部。当一个已经存在于缓存中的项目到达时,与该项目的键对应的节点被移动到列表的尾部。 插入一个新元素是 O(1),更新时间戳也是 O(n)(因为我们需要移动到列表的尾部),删除一个元素是 O(1)。

现在我有以下问题:

  1. 这些实现中哪一种更适合 LRUCache。

  2. 有没有其他方法可以实现LRU Cache。

  3. 在Java中,我应该使用HashMap来实现LRUCache

  4. 我看到了诸如实现通用 LRU 缓存之类的问题,也看到了诸如实现 LRU 缓存之类的问题。通用 LRU 缓存与 LRU 缓存不同吗?

提前致谢!!!

编辑:

在 Java 中实现 LRUCache 的另一种方法(最简单的方法)是使用 LinkedHashMap 并覆盖布尔 removeEldestEntry(Map.entry eldest) 函数。

【问题讨论】:

  • 在java中你应该使用LinkedHashMap并且几乎从不使用HashMap。最好的 LRU 映射使用某种形式的带有链接节点的映射,即它可以通过二分搜索红黑和链接(上一个/下一个)节点来实现,除了父/左/右/值/红色|黑色字段。或者 LinkedHashMap 是什么:带有 prev/next 的基于树的存储桶。
  • HashMap、HashTable、LinkedHashMap 在 Java 中是使用哈希表实现的,即用于解决冲突的数组和线性探测。它们不是作为红黑树实现的
  • 我从来没有告诉过它们是红黑的,你可以看到 TreeMap,在节点上添加 prev/next 并不难,我有一个类似的原始双精度图,即 red-black w/ prev/节点之间的下一个。
  • 是的,在树数据结构中添加 prev/next 并不难,但我想知道如果你想添加 prev/next 节点,你是否需要修改内部 TreeMap 类?

标签: java c++ caching


【解决方案1】:

如果你想要一个 LRU 缓存,Java 中最简单的就是 LinkedHashMap。默认行为是 FIFO,但是您可以将其更改为“访问顺序”,使其成为 LRU 缓存。

public static <K,V> Map<K,V> lruCache(final int maxSize) {
    return new LinkedHashMap<K, V>(maxSize*4/3, 0.75f, true) {
        @Override
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            return size() > maxSize;
        }
    };
}

注意:我使用 constructor 将集合从最新的优先更改为最近使用的优先。

来自 Javadoc

public LinkedHashMap(int initialCapacity,
                     float loadFactor,
                     boolean accessOrder)
Constructs an empty LinkedHashMap instance with the specified initial capacity, load factor and ordering mode.
Parameters:
initialCapacity - the initial capacity
loadFactor - the load factor
accessOrder - the ordering mode - true for access-order, false for insertion-order

当 accessOrder 为 true 时,只要您 get() 一个不是最后一个条目,LinkedHashMap 就会重新排列映射的顺序。

这样最旧的条目是最近使用最少的。

【讨论】:

  • 您的代码将删除最旧的条目,而不是最近使用的条目。最旧的条目也可以是最近使用的条目。
  • @AmrishPandey 当您使用将排序模式更改为“访问顺序”时,它会变成 LRU 缓存。 (见我更新的答案)
  • 优秀!!!不知道我们可以更改订单以访问订单。测试了这个,工作正常。大多数实现使用 hashmap 和 DLL 的组合
  • 为什么你用cacheSize作为(4/3 * cacheSize)而不是cacheSize,负载因子也是0.75f,当我们的HashMap不超过初始缓存大小(我们必须在固定的 HashMap 上迭代 n 个页面)。
  • @NikhilVerma 默认负载因子为 0.75,提高它可能会降低效率。负载因子为 0.75 时,您需要容量为 cacheSize/0.75cacheSize*4/3,否则地图可能会调整大小以确保其保持低于其负载因子。即容量 * loadFactor >= size().
【解决方案2】:

通常,LRU 缓存表示为 LIFO 结构 - 单个元素队列。如果您的标准提供的标准不允许您从中间移除对象,例如将它们放在顶部,那么您可能必须自己滚动。

【讨论】:

  • 更新时间戳的时间复杂度能否从O(n)提高到常数?
  • 是的,可以是O(1),看我上面的评论,Java中的LinkedHashMap是O(1)
  • LinkedHashMap 提供了一种遍历映射中存在的键的方法,但查找最近的键/值对将再次需要顺序搜索
  • @Amm,第一个(要删除的)是 O(1)。 map.entrySet().iterator().next() 或者你可以覆盖removeEldestEntry(Map.Entry&lt;K,V&gt; eldest),LinkedHashMap 不提供尾部(即最后添加/触摸的)和反向遍历,尽管它实际上被结构支持。
  • 如果要删除的项目位于列表中间的某个位置,那么您是否需要到达该元素才能删除它?
【解决方案3】:

考虑到缓存允许并发访问,好的LRU缓存设计问题归结为:

a) 我们能否在更新两个结构(缓存结构和 LRU 结构)时避免使用互斥锁。

b) 缓存的读(获取)操作是否需要互斥锁?

更详细地说:假设我们使用 java.util.concurrent.ConcuurentHashMap(cache structure) 和 java.util.concurrent.ConcurrentLinkedQueue(LRU structure) 实现这一点

a)在编辑操作中锁定这两个结构 - addEntry()、removeEntry()、evictEntries() 等。

b) 上面可能会通过慢写操作,但问题是即使是读(获取)操作,我们也需要在两个结构上应用锁。因为,get 意味着将条目放在 LRU 策略的队列前面。(假设条目从队列末尾删除)。

使用高效的并发结构,如 ConcurrentHashMap 和等待空闲的 ConcurrentLinkedQueue,然后对它们加锁,这将失去它的全部目的。

我使用相同的方法实现了 LRU 缓存,但是,LRU 结构是异步更新的,因此在访问这些结构时无需使用任何互斥锁。 LRU 是 Cache 的一个内部细节,可以以任何方式实现而不影响缓存的用户。

后来,我也读到了 ConcurrentLinkedHashMap

https://code.google.com/p/concurrentlinkedhashmap/

并发现它也在尝试做同样的事情。没用过这种结构,但也许很合适。

【讨论】:

    【解决方案4】:

    我想通过两种可能的实现来扩展其中的一些建议。一个不是线程安全的,一个可能是。

    这是一个最简单的版本,带有一个单元测试,表明它可以工作。

    首先是非并发版本:

    import java.util.LinkedHashMap;
    import java.util.Map;
    
    public class LruSimpleCache<K, V> implements LruCache <K, V>{
    
        Map<K, V> map = new LinkedHashMap (  );
    
    
        public LruSimpleCache (final int limit) {
               map = new LinkedHashMap <K, V> (16, 0.75f, true) {
                   @Override
                   protected boolean removeEldestEntry(final Map.Entry<K, V> eldest) {
                       return super.size() > limit;
                   }
               };
        }
        @Override
        public void put ( K key, V value ) {
            map.put ( key, value );
        }
    
        @Override
        public V get ( K key ) {
            return map.get(key);
        }
    
        //For testing only
        @Override
        public V getSilent ( K key ) {
            V value =  map.get ( key );
            if (value!=null) {
                map.remove ( key );
                map.put(key, value);
            }
            return value;
        }
    
        @Override
        public void remove ( K key ) {
            map.remove ( key );
        }
    
        @Override
        public int size () {
            return map.size ();
        }
    
        public String toString() {
            return map.toString ();
        }
    
    
    }
    

    true 标志将跟踪gets 和puts 的访问。请参阅 JavaDocs。构造函数没有 true 标志的 removeEdelstEntry 只会实现 FIFO 缓存(请参阅下面有关 FIFO 和 removeEldestEntry 的注释)。

    这是证明它可以用作 LRU 缓存的测试:

    public class LruSimpleTest {
    
        @Test
        public void test () {
            LruCache <Integer, Integer> cache = new LruSimpleCache<> ( 4 );
    
    
            cache.put ( 0, 0 );
            cache.put ( 1, 1 );
    
            cache.put ( 2, 2 );
            cache.put ( 3, 3 );
    
    
            boolean ok = cache.size () == 4 || die ( "size" + cache.size () );
    
    
            cache.put ( 4, 4 );
            cache.put ( 5, 5 );
            ok |= cache.size () == 4 || die ( "size" + cache.size () );
            ok |= cache.getSilent ( 2 ) == 2 || die ();
            ok |= cache.getSilent ( 3 ) == 3 || die ();
            ok |= cache.getSilent ( 4 ) == 4 || die ();
            ok |= cache.getSilent ( 5 ) == 5 || die ();
    
    
            cache.get ( 2 );
            cache.get ( 3 );
            cache.put ( 6, 6 );
            cache.put ( 7, 7 );
            ok |= cache.size () == 4 || die ( "size" + cache.size () );
            ok |= cache.getSilent ( 2 ) == 2 || die ();
            ok |= cache.getSilent ( 3 ) == 3 || die ();
            ok |= cache.getSilent ( 4 ) == null || die ();
            ok |= cache.getSilent ( 5 ) == null || die ();
    
    
            if ( !ok ) die ();
    
        }
    

    现在是并发版本...

    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class LruSimpleConcurrentCache<K, V> implements LruCache<K, V> {
    
        final CacheMap<K, V>[] cacheRegions;
    
    
        private static class CacheMap<K, V> extends LinkedHashMap<K, V> {
            private final ReadWriteLock readWriteLock;
            private final int limit;
    
            CacheMap ( final int limit, boolean fair ) {
                super ( 16, 0.75f, true );
                this.limit = limit;
                readWriteLock = new ReentrantReadWriteLock ( fair );
    
            }
    
            protected boolean removeEldestEntry ( final Map.Entry<K, V> eldest ) {
                return super.size () > limit;
            }
    
    
            @Override
            public V put ( K key, V value ) {
                readWriteLock.writeLock ().lock ();
    
                V old;
                try {
    
                    old = super.put ( key, value );
                } finally {
                    readWriteLock.writeLock ().unlock ();
                }
                return old;
    
            }
    
    
            @Override
            public V get ( Object key ) {
                readWriteLock.writeLock ().lock ();
                V value;
    
                try {
    
                    value = super.get ( key );
                } finally {
                    readWriteLock.writeLock ().unlock ();
                }
                return value;
            }
    
            @Override
            public V remove ( Object key ) {
    
                readWriteLock.writeLock ().lock ();
                V value;
    
                try {
    
                    value = super.remove ( key );
                } finally {
                    readWriteLock.writeLock ().unlock ();
                }
                return value;
    
            }
    
            public V getSilent ( K key ) {
                readWriteLock.writeLock ().lock ();
    
                V value;
    
                try {
    
                    value = this.get ( key );
                    if ( value != null ) {
                        this.remove ( key );
                        this.put ( key, value );
                    }
                } finally {
                    readWriteLock.writeLock ().unlock ();
                }
                return value;
    
            }
    
            public int size () {
                readWriteLock.readLock ().lock ();
                int size = -1;
                try {
                    size = super.size ();
                } finally {
                    readWriteLock.readLock ().unlock ();
                }
                return size;
            }
    
            public String toString () {
                readWriteLock.readLock ().lock ();
                String str;
                try {
                    str = super.toString ();
                } finally {
                    readWriteLock.readLock ().unlock ();
                }
                return str;
            }
    
    
        }
    
        public LruSimpleConcurrentCache ( final int limit, boolean fair ) {
            int cores = Runtime.getRuntime ().availableProcessors ();
            int stripeSize = cores < 2 ? 4 : cores * 2;
            cacheRegions = new CacheMap[ stripeSize ];
            for ( int index = 0; index < cacheRegions.length; index++ ) {
                cacheRegions[ index ] = new CacheMap<> ( limit / cacheRegions.length, fair );
            }
        }
    
        public LruSimpleConcurrentCache ( final int concurrency, final int limit, boolean fair ) {
    
            cacheRegions = new CacheMap[ concurrency ];
            for ( int index = 0; index < cacheRegions.length; index++ ) {
                cacheRegions[ index ] = new CacheMap<> ( limit / cacheRegions.length, fair );
            }
        }
    
        private int stripeIndex ( K key ) {
            int hashCode = key.hashCode () * 31;
            return hashCode % ( cacheRegions.length );
        }
    
        private CacheMap<K, V> map ( K key ) {
            return cacheRegions[ stripeIndex ( key ) ];
        }
    
        @Override
        public void put ( K key, V value ) {
    
            map ( key ).put ( key, value );
        }
    
        @Override
        public V get ( K key ) {
            return map ( key ).get ( key );
        }
    
        //For testing only
        @Override
        public V getSilent ( K key ) {
            return map ( key ).getSilent ( key );
    
        }
    
        @Override
        public void remove ( K key ) {
            map ( key ).remove ( key );
        }
    
        @Override
        public int size () {
            int size = 0;
            for ( CacheMap<K, V> cache : cacheRegions ) {
                size += cache.size ();
            }
            return size;
        }
    
        public String toString () {
    
            StringBuilder builder = new StringBuilder ();
            for ( CacheMap<K, V> cache : cacheRegions ) {
                builder.append ( cache.toString () ).append ( '\n' );
            }
    
            return builder.toString ();
        }
    
    
    }
    

    你可以看到我为什么先介绍非并发版本。以上尝试创建一些条带以减少锁争用。所以我们对键进行散列,然后查找该散列以找到实际的缓存。这使得限制大小更像是一个建议/粗略猜测,取决于您的密钥哈希算法的传播程度。

    【讨论】:

      【解决方案5】:

      问题陈述:

      创建 LRU 缓存并存储 Employee 对象 Max =5 个对象并找出谁先登录,然后...

      package com.test.example.dto;
      
      import java.sql.Timestamp;
      /**
       * 
       * @author Vaquar.khan@gmail.com
       *
       */
      public class Employee implements Comparable<Employee> {
          private int     id;
          private String  name;
          private int     age;
          private Timestamp loginTime ;
      
      public int getId() {
          return id;
      }
      
      public void setId(int id) {
          this.id = id;
      }
      
      public String getName() {
          return name;
      }
      
      public void setName(String name) {
          this.name = name;
      }
      
      public int getAge() {
          return age;
      }
      
      public void setAge(int age) {
          this.age = age;
      }
      
      public Timestamp getLoginTime() {
          return loginTime;
      }
      
      public void setLoginTime(Timestamp loginTime) {
          this.loginTime = loginTime;
      }
      
      @Override
      public String toString() {
          return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", loginTime=" + loginTime + "]";
      }
      
      Employee(){}
      
      public Employee(int id, String name, int age, Timestamp loginTime) {
          super();
          this.id = id;
          this.name = name;
          this.age = age;
          this.loginTime = loginTime;
      }
      
      @Override
      public int hashCode() {
          final int prime = 31;
          int result = 1;
          result = prime * result + age;
          result = prime * result + id;
          result = prime * result + ((loginTime == null) ? 0 : loginTime.hashCode());
          result = prime * result + ((name == null) ? 0 : name.hashCode());
          return result;
      }
      
      @Override
      public boolean equals(Object obj) {
          if (this == obj) return true;
          if (obj == null) return false;
          if (getClass() != obj.getClass()) return false;
          Employee other = (Employee) obj;
          if (age != other.age) return false;
          if (id != other.id) return false;
          if (loginTime == null) {
              if (other.loginTime != null) return false;
          } else if (!loginTime.equals(other.loginTime)) return false;
          if (name == null) {
              if (other.name != null) return false;
          } else if (!name.equals(other.name)) return false;
          return true;
      }
      
      @Override
      public int compareTo(Employee emp) {
          if (emp.getLoginTime().before( this.loginTime) ){
              return 1;
          } else if (emp.getLoginTime().after(this.loginTime)) {
              return -1;
          }
          return 0;
      }
      
      
      }
      

      LRUObjectCache 示例

      package com.test.example;
      
      import java.sql.Timestamp;
      import java.util.Calendar;
      import java.util.LinkedHashMap;
      import java.util.Map;
      import java.util.Map.Entry;
      import com.test.example.dto.Employee;
      /**
       * 
       * @author Vaquar.khan@gmail.com
       *
       */
      public class LRUObjectCacheExample {
      
      
          LinkedHashMap<Employee, Boolean>    lruCacheLinkedQueue;
      
      public LRUObjectCacheExample(int capacity) {
          lruCacheLinkedQueue = new LinkedHashMap<Employee, Boolean>(capacity, 1.0f, true) {
              /**
               * 
               */
              private static final long   serialVersionUID    = 1L;
      
              @Override
              protected boolean removeEldestEntry(
                      //calling map's entry method
                      Map.Entry<Employee, Boolean> eldest) {
                  return this.size() > capacity;
              }
          };
      }
      
      void addDataIntoCache(Employee employee) {
          lruCacheLinkedQueue.put(employee, true);
          displayLRUQueue();
      }
      
      boolean checkIfDataPresentIntoLRUCaache(int data) {
          return lruCacheLinkedQueue.get(data) != null;
      }
      
       void deletePageNo(int data) {
          if (lruCacheLinkedQueue.get(data) != null){
                  lruCacheLinkedQueue.remove(data);
          }
          displayLRUQueue();
      }
      
       void displayLRUQueue() {
          System.out.print("-------------------------------------------------------"+"\n");
          System.out.print("Data into LRU Cache : ");
          for (Entry<Employee, Boolean> mapEntry : lruCacheLinkedQueue.entrySet()) {
              System.out.print("[" + mapEntry.getKey() + "]");
          }
          System.out.println("");
      }
      
      public static void main(String args[]) {
          Employee employee1 = new Employee(1,"Shahbaz",29, getCurrentTimeStamp());
          Employee employee2 = new Employee(2,"Amit",35,getCurrentTimeStamp());
          Employee employee3 = new Employee(3,"viquar",36,getCurrentTimeStamp());
          Employee employee4 = new Employee(4,"Sunny",20,getCurrentTimeStamp());
          Employee employee5 = new Employee(5,"sachin",28,getCurrentTimeStamp());
          Employee employee6 = new Employee(6,"Sneha",25,getCurrentTimeStamp());
          Employee employee7 = new Employee(7,"chantan",19,getCurrentTimeStamp());
          Employee employee8 = new Employee(8,"nitin",22,getCurrentTimeStamp());
          Employee employee9 = new Employee(9,"sanuj",31,getCurrentTimeStamp());
          //
          LRUObjectCacheExample lru = new LRUObjectCacheExample(5);
          lru.addDataIntoCache(employee5);//sachin
          lru.addDataIntoCache(employee4);//Sunny
          lru.addDataIntoCache(employee3);//viquar
          lru.addDataIntoCache(employee2);//Amit
          lru.addDataIntoCache(employee1);//Shahbaz -----capacity reached
          //
              lru.addDataIntoCache(employee6);/Sneha
              lru.addDataIntoCache(employee7);//chantan
              lru.addDataIntoCache(employee8);//nitin
              lru.addDataIntoCache(employee9);//sanuj
              //
              lru.deletePageNo(3);
              lru.deletePageNo(4);
      
          }
          private static Timestamp getCurrentTimeStamp(){
              return new java.sql.Timestamp(Calendar.getInstance().getTime().getTime());
          }
      
      }
      

      结果:

      **Data into LRU Cache :** 
      [Employee [id=1, name=Shahbaz, age=29, loginTime=2015-10-15 18:47:28.1
      [Employee [id=6, name=Sneha, age=25, loginTime=2015-10-15 18:47:28.125
      [Employee [id=7, name=chantan, age=19, loginTime=2015-10-15 18:47:28.125
      [Employee [id=8, name=nitin, age=22, loginTime=2015-10-15 18:47:28.125
      [Employee [id=9, name=sanuj, age=31, loginTime=2015-10-15 18:47:28.125
      

      【讨论】:

        【解决方案6】:

        LinkedHashMap 允许您覆盖 removeEldestEntry 函数,以便在执行 put 时,您可以指定是否删除最旧的条目,从而允许实现 LRU。

        myLRUCache = new LinkedHashMap<Long,String>() {
        protected boolean removeEldestEntry(Map.Entry eldest) 
        { 
        if(this.size()>1000)
            return true;
          else
              return false;
        }
        }; 
        

        【讨论】:

          【解决方案7】:

          对于 O(1) 访问,我们需要一个哈希表,并且为了维护顺序,我们可以使用 DLL。 基本算法是 - 从页码我们可以使用哈希表到达 DLL 节点。如果页面存在,我们可以将节点移动到 DLL 的头部,否则将节点插入 DLL 和哈希表中。如果 DLL 的大小已满,我们可以从 tail 中删除最近最少使用的节点。

          这里是基于C++中的双向链表和unordered_map的实现。

          #include <iostream>
          #include <unordered_map>
          #include <utility>
          
          using namespace std;
          
          // List nodeclass
          class Node
          {
              public:
              int data;
              Node* next;
              Node* prev;
          };
          
          //Doubly Linked list
          class DLList
          {
              public:
          
              DLList()
              {
                  head = NULL;
                  tail = NULL;
                  count = 0;
              }
          
              ~DLList() {}
          
              Node* addNode(int val);
              void print();
              void removeTail();
              void moveToHead(Node* node);
          
              int size()
              {
                  return count;
              }
          
              private:
              Node* head;
              Node* tail;
              int count;
          };
          
          // Function to add a node to the list
          
          Node* DLList::addNode(int val)
          {
              Node* temp = new Node();
          
              temp->data = val;
              temp->next = NULL;
              temp->prev = NULL;
          
              if ( tail == NULL )
              {
                  tail = temp;
                  head = temp;
              }
              else
              {
                  head->prev = temp;
                  temp->next = head;
                  head = temp;
              }
          
              count++;
          
              return temp;
          }
          
          void DLList::moveToHead(Node* node)
          {
              if (head == node)
                  return;
          
              node->prev->next = node->next;
          
              if (node->next != NULL)
              {
                  node->next->prev = node->prev;
              }
              else
              {
                  tail = node->prev;
              }
                  node->next = head;
                  node->prev = NULL;
                  head->prev = node;
                  head = node;
          }
          
          void DLList::removeTail()
          {
              count--;
          
              if (head == tail)
              {
                  delete head;
                  head = NULL;
                  tail = NULL;
              }
              else
              {
                  Node* del = tail;
                  tail = del->prev;
                  tail->next = NULL;
                  delete del;
              }
          }
          
          void DLList::print()
          {
              Node* temp = head;
          
              int ctr = 0;
          
              while ( (temp != NULL) && (ctr++ != 25) )
              {
                  cout << temp->data << " ";
                  temp = temp->next;
              }
              cout << endl;
          }
          
          class LRUCache
          {
              public:
                  LRUCache(int aCacheSize);
                  void fetchPage(int pageNumber);
          
              private:
                  int cacheSize;
                  DLList dlist;
                  unordered_map< int, Node* > directAccess;
          };
          
              LRUCache::LRUCache(int aCacheSize):cacheSize(aCacheSize) { }
          
              void LRUCache::fetchPage(int pageNumber)
              {
                  unordered_map< int, Node* >::const_iterator it = directAccess.find(pageNumber);
          
                  if (it != directAccess.end())
                  {
                      dlist.moveToHead( (Node*)it->second);
                  }
                  else
                  {
                      if (dlist.size() == cacheSize-1)
                         dlist.removeTail();
          
                      Node* node = dlist.addNode(pageNumber);
          
                      directAccess.insert(pair< int, Node* >(pageNumber,node));
                  }
          
                  dlist.print();
              }
          
              int main()
              {
                  LRUCache lruCache(10);
          
                  lruCache.fetchPage(5);
                  lruCache.fetchPage(7);
                  lruCache.fetchPage(15);
                  lruCache.fetchPage(34);
                  lruCache.fetchPage(23);
                  lruCache.fetchPage(21);
                  lruCache.fetchPage(7);
                  lruCache.fetchPage(32);
                  lruCache.fetchPage(34);
                  lruCache.fetchPage(35);
                  lruCache.fetchPage(15);
                  lruCache.fetchPage(37);
                  lruCache.fetchPage(17);
                  lruCache.fetchPage(28);
                  lruCache.fetchPage(16);
          
                  return 0;
          }
          

          【讨论】:

            【解决方案8】:

            扩展 Peter Lawrey 的回答

            removeEldestEntry(java.util.Map.Entry) 方法可能会被覆盖,以便在向映射添加新映射时自动删除陈旧映射。

            所以可以重写LinkedHashMap的FIFO行为来制作LRU

            public class LRUCache<K, V> extends LinkedHashMap<K, V> {
            
                private int cacheSize;
            
                public LRUCache(int cacheSize) {
                    super(cacheSize * 4 / 3, 0.75f, true);
                    this.cacheSize = cacheSize;
                }
            
                @Override
                protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
                    return size() >= cacheSize;
                }
            
                public static void main(String args[]) {
                    LRUCache<Integer, Integer> lruCache = new LRUCache<>(5);
            
                    lruCache.put(1, 1);
                    lruCache.put(2, 2);
                    lruCache.put(3, 3);
                    lruCache.put(1, 4);
                    lruCache.put(2, 5);
                    lruCache.put(7, 6);
            
                    System.out.println(lruCache.keySet());
            
                    lruCache.put(1, 4);
                    lruCache.put(2, 5);
            
                    System.out.println(lruCache.keySet());
                }
            } 
            

            【讨论】:

            • 同样的评论:最老的条目也可能是最近使用的。我们需要删除最近最少使用而不是最旧的条目
            • 通过设置访问顺序标志 LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) 获得访问顺序(即lru)
            • 同意..收回我的评论
            【解决方案9】:
            class LRU {
                Map<String, Integer> ageMap = new HashMap<String, Integer>();
                int age = 1;
            
                void addElementWithAge(String element) {
            
                    ageMap.put(element, age);
                    age++;
            
                }
            
                String getLeastRecent(Map<String, Integer> ageMap) {
                    Integer oldestAge = (Integer) ageMap.values().toArray()[0];
                    String element = null;
                    for (Entry<String, Integer> entry : ageMap.entrySet()) {
                        if (oldestAge >= entry.getValue()) {
                            oldestAge = entry.getValue();
                            element = entry.getKey();
                        }
                    }
                    return element;
                }
            
                public static void main(String args[]) {
                    LRU obj = new LRU();
                    obj.addElementWithAge("M1");
                    obj.addElementWithAge("M2");
                    obj.addElementWithAge("M3");
                    obj.addElementWithAge("M4");
                    obj.addElementWithAge("M1");
                }
            
            }
            

            【讨论】:

              猜你喜欢
              • 2023-04-07
              • 2010-11-03
              • 2015-07-19
              • 1970-01-01
              • 2019-12-10
              • 2021-03-05
              • 2015-02-13
              • 1970-01-01
              相关资源
              最近更新 更多