Должен ли я использовать put() или putIfAbsent() после использования getOrDefault()?

Java8 представила эти хорошие методы getOrDefault() и putIfAbsent(), позволяющие писать код вроде:

Map<Foo, List<Bar>> itemsByFoo = ...
List<Bar> bars = itemsByFoo.getOrDefault(key, new ArrayList<>());
bars.add(someNewBar);

Теперь мне интересно, есть ли хорошие фактические причины:

itemsByFoo.put(key, bars);

или

itemsByFoo.putIfAbsent(key, bars);

Оба будут работать:

  • вариант 1 может сделать много ненужных "помеченных" вызовов при частом добавлении элементов в списки
  • option2 может сделать много ненужных вызовов "containsKey", когда добавление новых записей для новых ключей является доминирующим.

SO: это хорошие причины для выбора варианта 1 или варианта 2 "всегда"?

Ответ 1

getOrDefault подходит, если вы хотите использовать stand-in для отсутствующего значения без изменения карты. Если вы хотите добавить новое значение для отсутствующих ключей, вы можете сделать это правильно за одну операцию.

List<Bar> bars = itemsByFoo.computeIfAbsent(key, x -> new ArrayList<>());
bars.add(someNewBar);

или даже

itemsByFoo.computeIfAbsent(key, x -> new ArrayList<>()).add(someNewBar);

В лучшем случае при переопределении реализации Map, например, с HashMap, это будет иметь только один хэш-поиск.

Не то, что putIfAbsent имеет только два поиска при использовании реализации default, но, конечно, большинство реализаций Map предоставят для него единую реализацию поиска. Тем не менее, комбинация getOrDefault и putIfAbsent по-прежнему будет иметь два поиска в лучшем случае, тогда как оптимизированный computeIfAbsent выполняет только один.

Ответ 2

Важным моментом в computeIfAbsent является то, что он принимает Function, который будет выполняться только в том случае, если Key отсутствует, и нам нужен по умолчанию Value.

В то время как getOrDefault требуется сам по умолчанию Value, уже вычислен. В этом случае по умолчанию Value нам понадобится new ArrayList<Bar>(), который имеет побочный эффект выделения нового объекта в куче.

Мы хотим отложить это до тех пор, пока не будем уверены, что Key еще не находится в itemsByFoo. В противном случае мы собирали ненужный мусор для gc для сбора.