Карта, позволяющая поставитьIfAbsent без создания значения, когда ключ уже существует

Я хотел бы использовать карту, которая была бы эквивалентна ConcurrentMap (мне нужен эквивалент метода putIfAbsent), но это не заставляло бы меня создавать объект заранее.

Например, когда я делаю это:

m.putIfAbsent( key, new CyclingArray() );

Я могу в конечном итоге создать новый объект CyclingArray (что бы это ни было) ни за что.

Конечно, я понимаю, что могу заблокировать всю карту, но это лишило бы смысла ConcurrentMap.

Может ли что-то вроде следующего работать концептуально?

   m.putIfAbsent( key, new Callback<CyclingArray>() {
     @Override
     public CyclingArray provide() {
       return new CyclingArray();  // only called if the key wasn't already present
     }         
   }

Знаете ли вы какую-нибудь библиотеку, предлагающую карту, которая:

  1. предложить «интерфейс», подобный тому, который предлагает ConcurrentMap, включая метод putIfAbsent.
  2. блокирует только тот сегмент, с которым мы собираемся работать (как, например, реализация ConcurrentHashMap)
  3. позволяет дополнительно создать значение тогда и только тогда, когда ключ еще не присутствовал, и, следовательно, избежать бесполезной генерации мусора.
  4. не заставляет меня сначала использовать containsKey, а затем putIfAbsent, потому что это также каким-то образом противоречит цели putIfAbsent.

Обратите внимание, что я не спрашиваю, можно ли выполнить приведенный выше пример с помощью ConcurrentMap (это невозможно, насколько я знаю).

Я думал о расширении ConcurrentHashMap и перегрузке putIfAbsent версией обратного вызова, но, к сожалению, ConcurrentHashMap внутренне использует окончательный класс Segment.

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


person Cedric Martin    schedule 15.04.2012    source источник


Ответы (1)


Это обычный вариант использования, который вы ищете, он называется мемоизацией. Я бы посмотрел на MapMaker.

Вы сможете создать вычислительную карту и поместить туда свою функцию создания:

 ConcurrentMap<Key, CyclingArray> graphs = new MapMaker()
       .concurrencyLevel(32)
       .makeComputingMap(
           new Function<Key, CyclingArray>() {
                public CyclingArray  apply(Key key) {
                    return new CyclingArray();  // only called if the key wasn't already    
                }              
           });

Здесь Function будет вызываться только в том случае, если Key отсутствует.

И я знаю, что в планах на будущее в Java есть интерфейс типа computerMap, который будет поставляться со стандартной Java, к сожалению, на этом этапе вам придется делегировать google-коллекции.

person John Vint    schedule 15.04.2012
comment
+1... Но я всегда использовал термин мемоизация для обозначения кэширования на лету результатов вызовов методов: compute(17,24,59) принимает долгое время для выполнения, сохраните результат этого вычисления в кеше и повторно используйте его при попадании в кеш. Я никогда не слышал термин мемоизация, используемый при попытке предотвратить создание ненужных объектов. Здесь в Википедии en.wikipedia.org/wiki/Memoization определение и примеры не имеют ничего общего с созданием объекта. Я могу ошибаться, но мне кажется немного странным называть это мемоизацией :) - person Cedric Martin; 15.04.2012
comment
Просто для ясности: использование обратного вызова здесь, чтобы избежать создания объекта, по своей сути работает с функцией Java (назовем это так). Так что это правда, что мы можем в конечном итоге более или менее повторно использовать результат вызова метода, но в моей идее мемоизация, как всегда, заключалась в использовании параметров вызова метода для вычисления ключа на лету и сохранения результата в кеше. (в отличие от динамического программирования, которое обычно заранее кэширует все возможные результаты). - person Cedric Martin; 15.04.2012