Guava MapMaker дополнительно установить MaximumSize(0) для заводского метода?
Я использую MapMaker для реализации кэширования объектов данных в моем приложении:
public class DataObjectCache<DO extends MyDataObject> {
private final ConcurrentMap<String, DO> innerCache;
public DataObjectCache(Class<DO> doClass) {
Function<String, DO> loadFunction = new Function<String, DO>() {
@Override
public DO apply(String id) {
//load and return DO instance
}
};
innerCache = new MapMaker()
.softValues()
.makeComputingMap(loadFunction);
}
private DO getDataObject(String id) {
return innerCache.get(id);
}
private void putDataObject(DO dataObject) {
innerCache.putIfAbsent(dataObject.getID(), dataObject);
}
}
Один из этих DataObjectCaches будет создан для каждого класса объектов данных, и они будут храниться в главной карте, используя объекты Class в качестве ключей.
Существует меньшинство классов объектов данных, экземпляры которых я не хочу кэшировать. Однако я все еще хотел бы, чтобы они создавались в том же коде, который вызывает функция, и все еще нуждался бы в параллелизме в отношении их четкой загрузки.
В этих случаях мне интересно, могу ли я просто установить максимальный размер карты равным 0, чтобы записи немедленно выселялись, но при этом использовались преимущества атомных вычислений карты. Это хорошая идея? Неэффективное?
РЕДАКТИРОВАТЬ:
Я понял, что, если я вытеснил записи сразу после их загрузки, нет никакого способа гарантировать, что они отчетливо загружены - если Карта не отслеживает их, несколько экземпляров объекта с одинаковым идентификатором могут перемещаться по среде. Поэтому вместо того, чтобы делать это, я думаю, что я буду использовать слабые значения вместо мягких значений для типов объектов, которые я не хочу использовать в кеше - дайте мне знать, если у кого-то есть мнение по этому поводу.
2 ответа
В свете ваших правок, звучит так, будто вы ищете интернера. Интерн возвращает репрезентативный экземпляр; тот же объект будет возвращен Interner.intern
для всех объектов, которые равны по вашему equals
метод. Из Javadoc:
Выбирает и возвращает репрезентативный экземпляр для любого из набора экземпляров, которые равны друг другу. Если этому методу даны два равных входа, оба вызова вернут один и тот же экземпляр. То есть intern(a).equals (a) всегда выполняется, а intern(a) == intern(b) тогда и только тогда, когда a.equals(b). Обратите внимание, что intern(a) разрешено возвращать один экземпляр сейчас, а другой - позже, если исходный интернированный экземпляр был собран сборщиком мусора.
См. http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Interner.html и http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Interners.html
Тем не менее, это зависит от того, что вы имеете в виду, когда говорите, что не хотите, чтобы это кэшировалось. Если вы действительно хотите каждый раз возвращать новый экземпляр, вам нужно иметь несколько экземпляров эквивалентных объектов, "плавающих вокруг".
Interning удерживает экземпляр (чтобы он мог вернуть тот же), поэтому он все еще является своего рода кешем. Я хотел бы знать, почему вы хотите избежать кеширования. Если это из-за размера объектов, вы можете использовать слабый интернер; экземпляр будет доступен для GC, когда на него больше нет ссылок. Опять же, просто используя MapMaker
карта со слабыми значениями также сделает это.
Если, с другой стороны, причина, по которой вы не хотите кешировать, заключается в том, что ваши данные могут измениться, то вашим ответом может быть интернирование. Я хотел бы представить, что вы хотите, чтобы получить объект каждый раз, а затем интернировать его. Если объект равен кешированному, интернер просто вернет существующий экземпляр. Если это отличается, интернер кэширует новый. Ваша обязанность была бы написать equals
метод на вашем объекте, который отвечает требованиям для использования нового и интернированного экземпляра.
Что ж, MapMaker.maximumSize
имеет эту строку: checkArgument(size > 0, "maximum size must be positive")
, что сделает это невозможным. expireAfter
методы также требуют положительных аргументов. Совершенно очевидно, что разработчики API не хотели, чтобы вы использовали их MapMaker
карты сделаны таким образом.
Тем не менее, я полагаю, если вы действительно хотите использовать кеш в качестве прохода, вы можете использовать expireAfterWrite
1 наносекунды. Это взлом, но это практически даст тот же эффект.