The full name of LRU is Least Recently Used.
The design principle of LRU algorithm is: if a data has not been accessed in the recent period of time, it is also very unlikely to be accessed in the future. In other words, when the limited space is full of data, the data that has not been accessed for the longest time should be eliminated.
Implementation of LRU:
1. An array is used to store data, and each data item is marked with an access timestamp. Each time a new data item is inserted, the timestamp of the existing data item in the array is automatically increased, and the timestamp of the new data item is set to 0 and inserted into the array. Each time you access a data item in an array, set the timestamp of the accessed data item to 0. When the array space is full, the data item with the largest timestamp is eliminated.
2. Use a linked list to insert new data into the head of the linked list every time the data is newly inserted; move the data to the head of the linked list every time the cache hits (that is, the data is accessed); then discard the data at the end of the linked list when the linked list is full.
3. Using linked list and hashmap. When a new data item needs to be inserted, if the new data item exists in the linked list (generally called hit), move the node to the head of the linked list. If it does not exist, create a new node and put it in the head of the linked list. If the cache is full, delete the last node of the linked list. When accessing data, if the data item exists in the linked list, move the node to the head of the linked list, otherwise - 1 will be returned. In this way, the node at the end of the list is the data item that has not been accessed for the longest time.
Compare the advantages and disadvantages of the three methods:
For the first method, the access timestamp of data items needs to be maintained continuously. In addition, the time complexity is O(n) when inserting data, deleting data and accessing data. For the second method, the time complexity of linked list is O(n). So the third way is to implement LRU algorithm.
Implementation plan:
Using LinkedHashMap to implement
The underlying layer of linked HashMap is implemented by using HashMap plus double linked list, and it has realized the storage according to the access order. In addition, the LinkedHashMap itself implements a method, removealdestentry, to determine whether the least frequently read number needs to be removed. By default, the method returns false directly and does not remove elements. Therefore, the method needs to be overridden. That is, when the cache is full, the least commonly used number is removed.
import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @version v1.0 * @ClassName: LRULinkedHashMap * @Description: LRU Algorithm java implementation * @Date: 2020/1/11 17:03 */ public class LRULinkedHashMap<K,V> extends LinkedHashMap<K,V> { private int maxCapacity; private static final float DEFAULT_LOAD_FACTOR = 0.75f; private final Lock lock = new ReentrantLock(); /** * @param maxCapacity */ public LRULinkedHashMap(int maxCapacity) { super(maxCapacity, DEFAULT_LOAD_FACTOR, true); this.maxCapacity = maxCapacity; } /** * Method is used to determine whether the least frequently read number needs to be removed. The default value is false * @param eldest * @return */ @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > maxCapacity; } @Override public boolean containsKey(Object key) { try { lock.lock(); return super.containsKey(key); } finally { lock.unlock(); } } @Override public V get(Object key) { try { lock.lock(); return super.get(key); } finally { lock.unlock(); } } @Override public V put(K key, V value) { try { lock.lock(); return super.put(key, value); } finally { lock.unlock(); } } public int size() { try { lock.lock(); return super.size(); } finally { lock.unlock(); } } public void clear() { try { lock.lock(); super.clear(); } finally { lock.unlock(); } } public Collection<Map.Entry<K, V>> getAll() { try { lock.lock(); return new ArrayList<Map.Entry<K, V>>(super.entrySet()); } finally { lock.unlock(); } } }