Как получить LRU-кэш на базе LinkedHashMap?
- Нужен LRU-кэш на 1000 записей. Как сделать его на LinkedHashMap и за счёт чего это работает?
new LinkedHashMap<>(cap, 0.75f, true) (accessOrder) + переопределённый removeEldestEntry, возвращающий size() > max. Get переносит запись в конец, самая давно неиспользуемая — в голове и вытесняется.
- Без accessOrder получится FIFO, а не LRU
- Не потокобезопасно — для многих потоков Caffeine
- Всё за O(1)
А ещё Caffeine (W-TinyLFU) обычно даёт больше попаданий, чем LRU
Подробно
Нужны обе части. Флаг accessOrder переводит внутренний список из порядка вставки в порядок обращения: каждый get перемещает запись в конец, поэтому в голове списка всегда самая давно не используемая. Метод removeEldestEntry вызывается после вставки и по умолчанию возвращает false — пока его не переопределить, никакого вытеснения не будет. Ограничения, которые стоит назвать: реализация не потокобезопасна, а вытеснение происходит только при добавлении новой записи, поэтому карта, в которую перестали писать, не сжимается. Для конкурентного кэша с TTL берут специализированную библиотеку.
Пример
class Lru<K, V> extends LinkedHashMap<K, V> {
private final int max;
Lru(int max) { super(16, 0.75f, true); this.max = max; }
@Override protected boolean removeEldestEntry(Map.Entry<K, V> e) { return size() > max; }
}Что хотят услышать
- accessOrder и removeEldestEntry.
- Почему работает и ограничения.
Частые ошибки
- Задать accessOrder и забыть про removeEldestEntry.
- Использовать такой кэш из нескольких потоков без синхронизации.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Достаточно обычного конструктора: вытеснение старых записей работает по умолчанию
- ✗Переопределить equals и hashCode у ключа
- ✓Создать с accessOrder = true и переопределить removeEldestEntry, сравнивая размер с лимитом
- ✗Обернуть HashMap в Collections.synchronizedMap
Что спросят дальше
- Почему get меняет порядок при accessOrder = true?
- Когда стоит взять готовую библиотеку кэширования вместо этого приёма?
- Чем LRU отличается от LFU и когда какой выбирают?
- Как оценить, что кэш выбранного размера вообще помогает?
Разбор темы
- LinkedHashMap и LRU-кэш
Порядок вставки против порядка доступа, связанный список поверх HashMap и реализация LRU через removeEldestEntry.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.