LRU(Least Recently Used) 最近最少使用算法 在指定空间内, 存入元素时, 判断是否存在, 将其放置队列一端(如有取代旧元素). 当队满, 再添加新的元素时, 删除队列另一端的元素.
由于元素会频繁移动, 所以不能使用Array, 应考虑使用链表实现.
方案一
LinkedHashMap 是一个保存了元素插入顺序的HashMap.
accessOrder属性, 插入或获取元素时, 都会将其放入队尾.
removeEldestEntry方法, 是否删除最老的元素, 每次插入元素时都会被执行, 默认返回false.
创建子类
class LruLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
private int capacity;
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return this.size() > capacity;
}
LruLinkedHashMap(int capacity) {
super(16, 0.75F, true); // accessOrder 必须为 true
this.capacity = capacity;
}
}
测试
public static void main(String[] args) {
LruLinkedHashMap<String, String> map = new LruLinkedHashMap<>(4);
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
map.put("4", "4");
map.put("5", "5");
map.put("4", "7");
map.forEach((key, value) -> System.out.println(key + "->" + value));
}
结果
2->2
3->3
5->5
4->7
Process finished with exit code 0
方案二
自定义双向链表, Node内部存储previous和next, 比较浪费空间, 但是提升了效率. 也可以使用单向链表.
链表类节点类
class Node<K, V> {
private K key;
private V value;
private Node<K, V> previous, next;
public Node(K key, V value, Node<K, V> previous, Node<K, V> next) {
this.key = key;
this.value = value;
this.previous = previous;
this.next = next;
}
// getter and setter
...
}
链表类
构造方法指定容量, 提供get和put方法
class LRULink<K, V> {
private Node<K, V> head;
private Node<K, V> tail;
private int capacity;
private int count;
public LRULink(int capacity) {
this.capacity = capacity;
}
public Node<K, V> get(K key) {
if (head != null) {
Node<K, V> current = head;
do {
if (current.getKey() == key || current.getKey().equals(key)) {
if (head == current) {
return head;
} else if (current.getNext() == null) {
// 获取的元素在队尾
current.getPrevious().setNext(null);
} else {
// 获取的元素在队中
current.getPrevious().setNext(current.getNext());
current.getNext().setPrevious(current.getPrevious());
}
head.setPrevious(current);
current.setNext(head);
current.setPrevious(null);
head = current;
return current;
}
current = current.getNext();
} while (current != null && current.getNext() != null);
}
return null;
}
public void put(K key, V value) {
Node<K, V> node = new Node<>(key, value, null, null);
if (head == null) {
tail = head = node;
count = 1;
return;
}
Node<K, V> dummy = get(key);
if (dummy == null) {
// 添加元素
Node<K, V> oldHead = head;
oldHead.setPrevious(node);
node.setNext(oldHead);
head = node;
count++;
checkCapacity();
} else {
// 变更元素值
dummy.setValue(value);
}
}
// 超过容量之后, 从尾部循环删除
private void checkCapacity() {
while (count > capacity) {
tail = tail.getPrevious();
tail.setNext(null);
count--;
}
}
public Node<K, V> getHead() {
return head;
}
}
测试
public static void main(String[] args) {
LRULink<String, String> lruLink = new LRULink(5);
lruLink.put("1", "1");
lruLink.put("2", "2");
lruLink.put("4", "4");
lruLink.put("3", "3");
lruLink.put("5", "5");
lruLink.put("4", "7");
Node<String, String> node = lruLink.getHead();
do {
System.out.println(node.getKey() + "->" + node.getValue());
node = node.getNext();
} while (node != null && node.getNext() != null);
}
结果
4->7
5->5
3->3
2->2
Process finished with exit code 0