sta*_*010 13 java caching linked-list hashmap lru
我正在尝试实现自己的LRU缓存.是的,我知道Java 为此目的提供了LinkedHashMap,但我试图使用基本数据结构来实现它.
通过阅读这个主题,我明白我需要一个HashMap来O(1)查找一个密钥和一个链表来管理"最近最少使用的"驱逐策略.我发现这些引用都使用标准库hashmap但实现了自己的链表:
哈希表应该直接存储链接列表节点,如下所示.我的缓存应该存储Integer键和String值.
但是,在Java中,LinkedList集合不公开其内部节点,因此我无法将它们存储在HashMap中.我可以将HashMap存储索引放入LinkedList中,但是到达一个项目需要O(N)时间.所以我试着存储一个ListIterator.
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedList;
import java.util.ListIterator;
public class LRUCache {
private static final int DEFAULT_MAX_CAPACITY = 10;
protected Map<Integer, ListIterator> _map = new HashMap<Integer, ListIterator>();
protected LinkedList<String> _list = new LinkedList<String>();
protected int _size = 0;
protected int _maxCapacity = 0;
public LRUCache(int maxCapacity) {
_maxCapacity = maxCapacity;
}
// Put the key, value pair into the LRU cache.
// The value is placed at the head of the linked list.
public void put(int key, String value) {
// Check to see if the key is already in the cache.
ListIterator iter = _map.get(key);
if (iter != null) {
// Key already exists, so remove it from the list.
iter.remove(); // Problem 1: ConcurrentModificationException!
}
// Add the new value to the front of the list.
_list.addFirst(value);
_map.put(key, _list.listIterator(0));
_size++;
// Check if we have exceeded the capacity.
if (_size > _maxCapacity) {
// Remove the least recently used item from the tail of the list.
_list.removeLast();
}
}
// Get the value associated with the key.
// Move value to the head of the linked list.
public String get(int key) {
String result = null;
ListIterator iter = _map.get(key);
if (iter != null) {
//result = iter
// Problem 2: HOW DO I GET THE STRING FROM THE ITERATOR?
}
return result;
}
public static void main(String argv[]) throws Exception {
LRUCache lruCache = new LRUCache(10);
lruCache.put(10, "This");
lruCache.put(20, "is");
lruCache.put(30, "a");
lruCache.put(40, "test");
lruCache.put(30, "some"); // Causes ConcurrentModificationException
}
}
Run Code Online (Sandbox Code Playgroud)
所以这导致了三个问题:
问题1:当我使用存储在HashMap中的迭代器更新LinkedList时,我收到了ConcurrentModificationException.
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:953)
at java.util.LinkedList$ListItr.remove(LinkedList.java:919)
at LRUCache.put(LRUCache.java:31)
at LRUCache.main(LRUCache.java:71)
Run Code Online (Sandbox Code Playgroud)
问题2.如何检索ListIterator指向的值?看来我只能检索next()值.
问题3.有没有办法使用Java集合LinkedList实现这个LRU缓存,还是我真的必须实现自己的链表?
我先解决问题3:
正如您在问题中指出的那样,LinkedList(就像所有精心设计的通用集合一样)隐藏了实现的细节,例如包含链接的节点。在您的情况下,您需要哈希映射来直接引用这些链接作为映射的值。否则(例如通过第三类进行间接访问)将违背 LRU 缓存的目的,即允许非常低的值访问开销。但这对于标准 Java 集合来说是不可能的 - 它们不(也不应该)提供对内部结构的直接访问。
因此,逻辑结论是,是的,您需要实现自己的方式来存储缓存中项目的使用顺序。这不一定是双链表。这些传统上用于 LRU 缓存,因为最常见的操作是在访问节点时将其移动到列表的顶部。在双链表中,这是一个非常便宜的操作,只需要重新链接四个节点,无需分配或释放内存。
问题1和2:
本质上,这里的根本原因是您尝试使用迭代器作为游标。它们被设计为被创建、逐步执行某些操作,然后被处理。即使你克服了所遇到的问题,我预计它们背后还会有更多的问题。您正在将一个方形钉子放入一个圆孔中。
所以我的结论是,您需要实现自己的方式来在跟踪访问顺序的类中保存值。然而它可以非常简单:只需要三个操作:创建、获取值和从尾部删除。创建和获取值都必须将节点移动到链表的头部。不能从列表中间插入或删除。没有删除头部。没有搜索。老实说非常简单。
希望这能让您开始:-)
public class <K,V> LRU_Map implements Map<K,V> {
private class Node {
private final V value;
private Node previous = null;
private Node next = null;
public Node(V value) {
this.value = value;
touch();
if (tail == null)
tail = this;
}
public V getValue() {
touch();
return value;
}
private void touch() {
if (head != this) {
unlink();
moveToHead();
}
}
private void unlink() {
if (tail == this)
tail = prev;
if (prev != null)
prev.next = next;
if (next != null)
next.prev = prev;
}
private void moveToHead() {
prev = null;
next = head;
head = this;
}
public void remove() {
assert this == tail;
assert this != head;
assert next == null;
if (prev != null)
prev.next = null;
tail = prev;
}
}
private final Map<K,Node> map = new HashMap<>();
private Node head = null;
private Node tail = null;
public void put(K key, V value) {
if (map.size() >= MAX_SIZE) {
assert tail != null;
tail.remove();
}
map.put(key, new Node(value));
}
public V get(K key) {
if (map.containsKey(key))
return map.get(key).getValue();
else
return null;
}
// and so on for other Map methods
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
595 次 |
| 最近记录: |