Должен ли я использовать 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
для сбора.