#java #caching #guava
#java #кэширование #гуава
Вопрос:
Мне нужно получить результат медленного метода slowResult()
для экземпляров класса Something
. Одно только кэширование не помогает, поскольку экземпляры практически никогда не повторяются. К счастью, я знаю, что результат на самом деле зависит только от некоторого легко получаемого Attributes
of Something
, поэтому я мог бы использовать a Map<Attributes, Result>
в качестве кэша в коде
Result fastResult(Something something) {
final Attributes attributes = attributesFor(something)
Result result = cache.get(attributes);
if (result!=null) return resu<
result = something.slowResult();
cache.put(attributes, result);
return resu<
}
Чтобы ограничить объем кэша, я использовал устаревшие методы maximumSize
и expireAfterAccess
. Устаревание происходит из-за нового класса CacheBuilder
, который будет использоваться вместо него. Однако я не нашел хорошего способа применить его к моему случаю. Ключи кэша должны быть экземплярами Attributes
, но при загрузке кэша Something
необходим экземпляр, и нет простого способа воссоздать его из Attributes
.
Есть ли хорошее решение этой проблемы?
Ответ №1:
Очень интересно! Я не думаю, что мы когда-либо слышали о подобном случае.
Для 11.0 вы сможете это сделать cache.asMap().put()
, так что, вероятно, было бы хорошо просто игнорировать предупреждения об устаревании на данный момент. Мы рассматриваем возможность включения этого в выпуске исправления 10.1, потому что несколько пользователей, похоже, находятся в похожей ситуации.
Но в 11.0 другая идея, которую мы обсуждаем, позволит вам сделать это:
Result fastResult(final Something something) {
return cache.get(
attributesFor(something),
new Callable<Result>() {
public Result call() {
return something.slowResult();
}
});
}
Комментарии:
1. Использование
cache.asMap().put
также требует предварительной проверкиcontains
, что для меня нормально. Два аргументаput
выглядят лучше. На самом деле, идеальным решением для меня было быCache.get2(K key, A attributes)
, гдеkey
иattributes
будет использоваться для поиска и передаваться загрузчику соответственно. ТакимCacheLoader
образом, параметр первого типа может отличаться от типа ключа. Но я не думаю, что он будет использоваться достаточно часто.
Ответ №2:
Ну, один из способов сформулировать эту проблему: «как я могу сравнить Attributes.equals()
кеш, но при этом иметь доступ к Something
для создания значения». Итак, я думаю, это может сработать…
class SomethingKey {
private Something something;
private final Attributes attributes;
SomethingKey(Something s) {
this.something = s;
this.attributes = attributesFor(something);
}
public Result createResult() {
Something something = this.s;
this.s = null;
return something.slowResult();
}
public boolean equals(Object o) {
if(o==this) return true;
if(o==null || o.getClass() != this.getClass) return false;
SomethingKey that = (SomethingKey)o;
return this.attributes.equals(that.attributes);
}
}
И тогда CacheLoader.load
в основном
Result load(SomethingKey k) {
return k.createResult();
}
И карта будет
Cache<SomethingKey,Result> map = ...;
Конечно, недостатком является то, что вы создаете новый SomethingKey для каждого запроса, но…
Комментарии:
1. В моем случае это нормально, из-за огромной стоимости вызова
slowResult
. ЕслиSomething
бы это был огромный объект, это тоже было бы проблемой.
Ответ №3:
Можете ли вы просто отключить кеш от чего-либо вместо этого?
Комментарии:
1. Нет … как я уже писал, «результат на самом деле зависит только от некоторых легко полученных
Attributes
Something
«. ВсякийAttributes
раз, когда мне нужно вернуть то жеResult
самое, кэшированиеSomething
не помогает, поскольку оно (в отличие отAttributes
полученного из него) почти никогда не повторяется.