Я пытаюсь реализовать свой собственный кеш LRU. Да, я знаю, что Java предоставляет LinkedHashMap для этой цели, но я пытаюсь реализовать это, используя базовые структуры данных.
Прочитав эту тему, я понял, что мне нужен поиск ключа HashMap для O (1) и связанный список для управления политикой выселения "наименее недавно использовавшейся". Я нашел эти ссылки, которые все используют хэш-карту стандартной библиотеки, но реализуют свой собственный связанный список:
- "Какие структуры данных обычно используется для кэшей LRU и быстрого поиска объектов? "(stackoverflow.com)
- "Как лучше всего реализовать кэш LRU? "(quora.com)
- "Реализовать кэш LRU на C ++" (uml. edu)
- "LRU Cache (Java)" (programcreek.com )
Предполагается, что хеш-таблица напрямую хранит узел связанного списка, как показано ниже. Мой кеш должен хранить целочисленные ключи и строковые значения.
Однако в 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
}
}
Это приводит к трем проблемам:
Проблема 1: я получаю исключение ConcurrentModificationException, когда обновляю LinkedList с помощью итератора, который я храню в HashMap.
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)
Проблема 2. Как мне получить значение, на которое указывает ListIterator? Кажется, я могу получить только значение next ().
Проблема 3. Есть ли способ реализовать этот кеш LRU с использованием LinkedList коллекций Java, или мне действительно нужно реализовать свой собственный связанный список?