Какую коллекцию использовать для хранения кэша с быстрым доступом по ключу в Java
Что проверяют
Отличаете ли вы «быстрый доступ по ключу» от настоящего кэша, у которого есть ограничение размера, вытеснение и работа из нескольких потоков. Следом обычно спрашивают, как сделать LRU на стандартных коллекциях и что будет, если кэш ничем не ограничивать.
Как отвечать
Быстрый доступ по ключу даёт хэш-таблица, дальше выбор зависит от требований:
HashMap: O(1) в среднем, но не потокобезопасна. Годится для кэша внутри одного потока;ConcurrentHashMap: основа кэша в многопоточном сервисе.computeIfAbsentатомарно вычисляет значение один раз на ключ и снимает гонку «проверил, потом положил»;LinkedHashMapсaccessOrder = trueи переопределённымremoveEldestEntry: простой LRU в несколько строк. Не синхронизирован, в многопоточном коде его придётся закрывать блокировкой;WeakHashMap: держит ключи через слабые ссылки, запись исчезает, когда на ключ больше нет сильных ссылок. К нехватке памяти это не привязано;- Caffeine: готовый кэш с
maximumSize,expireAfterWrite,refreshAfterWriteи статистикой попаданий. Алгоритм вытеснения W-TinyLFU обычно даёт больше попаданий, чем LRU. В Spring подключается как провайдер для@Cacheable.
Итог скажите прямо: коллекция из JDK подходит для учебной задачи или крошечного справочника, а в продакшн-сервисе берут Caffeine, потому что ограничение размера, TTL и конкурентный доступ там уже решены. Если кэш должен быть общим для нескольких экземпляров сервиса, это уже Redis, а не коллекция в памяти процесса. И про ключ: он должен быть неизменяемым, с корректными equals и hashCode.
Где спотыкаются
Кладут всё в HashMap без ограничения. Кэш растёт до OutOfMemoryError, это классическая утечка памяти в долгоживущем сервисе.
Рассказывают, что WeakHashMap чистится при нехватке памяти. Так ведёт себя SoftReference, а слабые ключи уходят при сборке мусора после потери сильных ссылок, независимо от запаса памяти.
Пишут if (!map.containsKey(k)) map.put(k, load(k)) на ConcurrentHashMap. Каждая операция по отдельности потокобезопасна, а их пара нет: несколько потоков одновременно пойдут загружать одно и то же значение.