Для кэша с поддержкой памяти вы можете использовать Коллекции Apache Commons , в частности их класс org.apache.commons.collections.map.ReferenceMap
. Специальная операция Java - это мягкая ссылка . Java предоставляет WeakHashMap
для слабых ссылок, но слабые ссылки - это не то, что вам нужно для кэша. Java не предоставляет SoftHashMap
, но ReferenceMap
от Apache Commons может быть подходящей заменой.
Осознание в памяти мягких ссылок несколько грубовато и негибко. Вы можете поиграть с некоторыми опциями Java, чтобы как-то их настроить, особенно значение -XX:SoftRefLRUPolicyMSPerMB
, которое выражает (в миллисекундах), как долго значения с мягкой ссылкой хранятся в памяти (когда они перестают быть непосредственно доступными). Например, с этим:
java -XX:SoftRefLRUPolicyMSPerMB=2500
тогда JVM попытается сохранить кэшированное значение на 2,5 секунды больше, чем это было бы с WeakHashMap
.
Если мягкие ссылки не предоставляют то, что вы ищете, вам придется реализовать собственную стратегию кэширования и, действительно, сбросить карту вручную. Это ваш начальный вопрос. Для очистки вы можете использовать метод clear()
или просто создать новый HashMap
. Разница должна быть незначительной, и у вас могут возникнуть проблемы, просто измерив эту разницу.
Чередование между «полным кешем» и «пустым кешем» также может считаться немного грубым, поэтому вы можете поддерживать несколько карт. Например, вы поддерживаете десять карт. Когда вы ищете кэшированное значение, вы просматриваете все карты, но когда у вас есть значение, вы помещаете его только в первую карту. Когда вы хотите сбросить карту, вы поворачиваете карты: первая карта становится второй, вторая становится третьей и так далее, вплоть до десятой карты, которая сбрасывается. Новая свежая первая карта создана. Это будет выглядеть так:
import java.util.*;
public class Cache {
private static final int MAX_SIZE = 500000;
private Map[] backend;
private int size = 0;
public Cache(int n)
{
backend = new Map[n];
for (int i = 0; i < n; i ++)
backend[i] = new HashMap();
}
public int size()
{
return size;
}
public Object get(Object key)
{
for (Map m : backend) {
if (m.containsKey(key))
return m.get(key);
}
return null;
}
public Object put(Object key, Object value)
{
if (backend[0].containsKey(key))
return backend[0].put(key, value);
int n = backend.length;
for (int i = 1; i < n; i ++) {
Map m = backend[i];
if (m.containsKey(key)) {
Object old = m.remove(key);
backend[0].put(key, value);
return old;
}
}
backend[0].put(key, value);
size ++;
while (size > MAX_SIZE) {
size -= backend[n - 1].size();
System.arraycopy(backend, 0, backend, 1, n - 1);
backend[0] = new HashMap();
}
return null;
}
}
Приведенный выше код полностью не проверен и должен быть дополнен обобщениями. Тем не менее, он иллюстрирует основные идеи: все карты тестируются при чтении (get()
), все новые значения переходят на первую карту, общий размер сохраняется, а когда размер превышает заданный предел, карты поворачиваются. Обратите внимание, что существует специальная обработка, когда новое значение устанавливается для известного ключа. Кроме того, в этой версии ничего особенного не делается при поиске кэшированного значения, но мы можем «омолодить» доступное кэшированное значение: при get()
, когда значение найдено, но не на первой карте, оно может быть перемещено в первую карта. Таким образом, часто используемые значения остаются в кэше навсегда.