Картограф Guava необязательно устанавливает максимальный размер (0) для заводского метода?

#java #guava

#java #guava

Вопрос:

Я использую 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 будет создан для каждого класса объектов данных, и они будут храниться на главной карте, используя объекты класса в качестве ключей.

Существует меньшинство классов объектов данных, экземпляры которых я не хочу кэшировать. Однако я все равно хотел бы, чтобы они создавались одним и тем же кодом, который вызывает функция, и все равно требовался бы параллелизм в отношении их четкой загрузки.

В этих случаях мне интересно, могу ли я просто установить максимальный размер карты равным 0, чтобы записи немедленно удалялись, но при этом использовать преимущества аспектов карты, связанных с атомарными вычислениями. Это хорошая идея? Неэффективно?

Редактировать:

Я понял, что если я удалю записи сразу после их загрузки, нет способа гарантировать, что они загружены четко — если карта не отслеживает их, несколько экземпляров объекта с одинаковым идентификатором могут перемещаться по среде. Поэтому вместо этого, я думаю, я буду использовать слабые значения вместо мягких значений для типов объектов, которые я не хочу занимать кэш — дайте мне знать, если у кого-нибудь есть мнение по этому поводу.

Ответ №1:

В свете вашей правки, похоже, что вы ищете интернера. Интернер возвращает репрезентативный экземпляр; один и тот же объект будет возвращен 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

Тем не менее, это зависит от того, что вы имеете в виду, когда говорите, что не хотите, чтобы это кэшировалось. Если вы действительно хотите каждый раз возвращать новый экземпляр, тогда вам придется иметь несколько экземпляров эквивалентных объектов, «плавающих вокруг».

При интернировании сохраняется экземпляр (поэтому он может возвращать тот же экземпляр), так что это все еще своего рода кэш. Я хотел бы знать, почему вы хотите избежать кэширования. Если это из-за размера объектов, вы можете использовать слабый интернер; экземпляр будет доступен для GC, когда на него больше не будет ссылок. Опять же, простое использование MapMaker карты со слабыми значениями также позволило бы достичь этого.

Если, с другой стороны, причина, по которой вы не хотите кэшировать, заключается в том, что ваши данные могут измениться, интернирование может быть вашим ответом. Я бы предположил, что вы хотели бы извлекать объект каждый раз, а затем интернировать его. Если объект равен кешированному, то интернер просто вернет существующий экземпляр. Если он отличается, программа-интернер будет кэшировать новый. В таком случае вашей обязанностью будет написать equals метод для вашего объекта, который отвечает требованиям для использования нового экземпляра vs interned.

Комментарии:

1. О, это интересно. Итак, вы предлагаете мне использовать интернер для типов объектов данных, которые я не хочу кэшировать, если я правильно понимаю (карты все равно будут использоваться для кэшированных типов). И я предполагаю, что это был бы слабый интернер, верно?

2. Я добавил в свой ответ, чтобы попытаться ответить на ваш комментарий. Дайте мне знать, если вам нужны дополнительные разъяснения

3. Спасибо, что помогли мне с этим. Вы правы, делая различие в том, что интернер будет своего рода кэшем. Когда я говорю, что есть типы объектов, которые я не хочу кэшировать, я просто имею в виду, что я знаю, что эти объекты обычно используются один раз, и нет смысла хранить их дольше, чем на них ссылаются. Я думаю, что буду придерживаться Map для всех типов и просто использовать слабые значения вместо soft для этих одноразовых типов. Спасибо, что научили меня о интернерах 1

Ответ №2:

Ну, MapMaker.maximumSize есть эта строка: checkArgument(size > 0, "maximum size must be positive") , которая сделает это невозможным. expireAfter Методы также требуют положительных аргументов. Совершенно очевидно, что разработчики API не хотели, чтобы вы использовали их MapMaker созданные карты таким образом.

Тем не менее, я полагаю, если вы действительно хотите использовать кэш в качестве сквозного, вы могли бы использовать expireAfterWrite 1 наносекунду. Это взлом, но он практически имел бы тот же эффект.

Комментарии:

1. На самом деле, похоже, что это было изменено в выпуске 09 и теперь специально вызывается как нечто, что вы можете сделать: «Когда размер равен нулю, элементы могут быть успешно добавлены на карту, но немедленно удаляются».

2. да, я рассмотрел этот подход, увидев это. Но я понял, что это не соответствует моим требованиям, см. Мою правку выше.