Может ли CacheBuilder заменить MapMaker в моем случае?

#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 полученного из него) почти никогда не повторяется.