Ответ
Самый простой способ — использовать HashMap для хранения данных в памяти.
public class SimpleCache<K, V> {
private final Map<K, V> cache = new HashMap<>();
public void put(K key, V value) {
cache.put(key, value);
}
public V get(K key) {
return cache.get(key);
}
public void remove(K key) {
cache.remove(key);
}
}
Почему это просто, но не всегда достаточно:
- Плюсы: Минимальная реализация, быстрый доступ O(1).
- Минусы:
- Нет ограничения размера (риск
OutOfMemoryError). - Нет политики вытеснения (например, LRU).
- Нет TTL (Time-To-Live) для устаревания записей.
- Не потокобезопасно для
HashMap.
- Нет ограничения размера (риск
Практические улучшения:
- Для потокобезопасности используйте
ConcurrentHashMap. - Для production-систем применяйте специализированные библиотеки:
- Caffeine: Высокопроизводительный, современный кэш.
- Guava Cache: От Google, богатый API (размер, TTL, статистика).
Пример с ConcurrentHashMap и LRU-подобным поведением:
public class SimpleConcurrentCache<K, V> {
private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();
private final int maxSize;
private final LinkedBlockingQueue<K> order = new LinkedBlockingQueue<>();
public SimpleConcurrentCache(int maxSize) { this.maxSize = maxSize; }
public void put(K key, V value) {
if (cache.size() >= maxSize) {
K oldestKey = order.poll(); // Удаляем самый старый ключ
if (oldestKey != null) cache.remove(oldestKey);
}
cache.put(key, value);
order.offer(key);
}
// ... get, remove
} Ответ 18+ 🔞
Ага, смотри, самый примитивный способ — это взять HashMap и просто туда всё пихать, как в помойное ведро.
public class SimpleCache<K, V> {
private final Map<K, V> cache = new HashMap<>();
public void put(K key, V value) {
cache.put(key, value);
}
public V get(K key) {
return cache.get(key);
}
public void remove(K key) {
cache.remove(key);
}
}
Ну и что тут такого, спросишь ты? А то, что это как взять одноразовую тарелку на всю жизнь — вроде работает, пока не начнёт протекать, блядь.
- Плюсы: Проще некуда, доступ быстрый — O(1), как удар током.
- Минусы, ёпта, их овердохуища:
- Размер не ограничен. Закидывай данные, пока не лопнет, как жадный удав на ферме мышей.
OutOfMemoryErrorтебе в сраку и привет. - Никакой политики вытеснения. Кто зашёл первым, тот и сидит вечно, как бабка на лавочке. Никакого LRU, LFU — нихуя.
- TTL? А что это? Записи не стареют, они тут бессмертные, как вампиры-алкаши.
- Потокобезопасность? Да какая, на хуй, потокобезопасность у обычного
HashMap? Это как зайти в общий сортир без дверей — всё закончится пиздецом.
- Размер не ограничен. Закидывай данные, пока не лопнет, как жадный удав на ферме мышей.
Ну а если по-взрослому, то:
- Чтобы не было драк между потоками, бери
ConcurrentHashMap. Это уже как сортир с кабинками. - А для продакшена, где всё серьёзно, лучше вообще не выёбывайся и бери готовые библиотеки:
- Caffeine: Быстрый, современный, прокачанный. Как спортивная тачка.
- Guava Cache: От гугла, с кучей прибамбасов — и размер задать можно, и время жизни записи, и статистику посмотреть.
Вот, смотри, примерчик чутка поумнее, с ConcurrentHashMap и намёком на LRU (хоть и кривоватым):
public class SimpleConcurrentCache<K, V> {
private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();
private final int maxSize;
private final LinkedBlockingQueue<K> order = new LinkedBlockingQueue<>();
public SimpleConcurrentCache(int maxSize) { this.maxSize = maxSize; }
public void put(K key, V value) {
if (cache.size() >= maxSize) {
K oldestKey = order.poll(); // Выковыриваем того, кто дольше всех сидит
if (oldestKey != null) cache.remove(oldestKey);
}
cache.put(key, value);
order.offer(key);
}
// ... get, remove
}
Но это всё равно, блядь, велосипед с квадратными колёсами. Для учебки сойдёт, а так — бери готовое, не мудри.