Java map.get (ключ) - автоматически делать put (ключ) и возвращать, если ключ не существует? - PullRequest
44 голосов
/ 01 декабря 2011

Мне надоел следующий шаблон:

value = map.get(key);
if (value == null) {
    value = new Object();
    map.put(key, value);
}

В этом примере царапается только поверхность дополнительного кода, который будет написан, когда у вас есть вложенные карты для представления многомерной структуры.

Я уверен, что где-то есть что-то, чтобы избежать этого, но мои усилия по поиску в Google ничего не дали.Есть предложения?

Ответы [ 7 ]

38 голосов
/ 01 декабря 2011

java.util.concurrent.ConcurrentMap 

и из Java 8

Java.util.Map

имеет

putIfAbsent(K key, V value) 

, который возвращает значение, сопоставленное ключу, или вставляет указанное значение и нольесли значение не сопоставлено для ключа.

Если вам нужна ленивая оценка значения, есть

computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
26 голосов
/ 15 апреля 2016

Java 8 добавляет хороший метод к Map : compute , computeIfPresent , computeIfAbsent

Для достижения чеговам нужно:

Object existingOrCreated = map.computeIfAbsent(key, (k) -> new Object());
3 голосов
/ 26 марта 2016

Вы можете использовать MutableMap и getIfAbsentPut() из Eclipse Collections , которая возвращает значение, сопоставленное ключу, или вставляет указанное значение и возвращает данное значение, если никакое значение не сопоставленоключ.

Вы можете использовать ссылку на метод для создания нового Object:

MutableMap<String, Object> map = Maps.mutable.empty();
Object value = map.getIfAbsentPut("key", Object::new);

Или вы можете напрямую создать новый Object:

MutableMap<String, Object> map = Maps.mutable.empty();    
Object value = map.getIfAbsentPut("key", new Object());

В первом примере объект будет создан, только если нет значения, сопоставленного с ключом.

Во втором примере объект будет создан независимо от других.

Примечание: я участвую в коллекциях Eclipse.

3 голосов
/ 01 декабря 2011

Проблема с этим шаблоном заключается в том, что вам нужно как-то определить значение, которое следует использовать в случае, если get() возвращает ноль.

Конечно, есть библиотеки, и IIRC есть и более новые коллекции, которые делают это, но, к сожалению, я не помню, какие это были.

Однако вы можете написать служебный метод самостоятельно, при условии, что у вас есть стандартный способ создания новых значений. Примерно так может работать:

public static <K, V> V safeGet(K key, Map<K,V> map, Class<V> valueClass) throws /*add exceptions*/ {
  V value = map.get(key);
  if( value == null ) {
    value = valueClass.newInstance();
    map.put( key, value );
  }   

  return value;
} 

Обратите внимание, что вы должны либо выбросить исключения отражения, либо обработать их в методе. Кроме того, для этого требуется valueClass для предоставления конструктора без аргументов. Кроме того, вы можете просто передать значение по умолчанию, которое следует использовать.

1 голос
/ 13 июля 2017

Если в любом случае вам нужно получить данные по умолчанию на вашей карте, если она не существует

map.getOrDefault(key, defaultValue);

javadocs

1 голос
/ 01 декабря 2011

РЕДАКТИРОВАНИЕ: Обратите внимание, что упомянутая ниже функция давно устарела, и вместо нее следует использовать CacheBuilder .

Библиотека Guava имеет " вычислительная карта ", см. MapMaker.makeComputingMap (Function) .

Map<String, Object> map = new MapMaker().makeComputingMap(
    new Function<String, Object>() {
      public String apply(Stringt) {
        return new Object();
      }
  });

Если вам нужна функция несколько раз, распакуйте ее в служебный класси затем создайте карту следующим образом (где MyFunctions.NEW_OBJECT - это статический экземпляр функции):

Map<String, Object> map = new MapMaker()
    .makeComputingMap(MyFunctions.NEW_OBJECT);
0 голосов
/ 01 декабря 2011

Может быть, я не вижу всей проблемы, но как насчет использования наследования или композиции для добавления этого поведения в объект Map?

...