ConcurrentHashMap JDK 8 когда использовать computeIfPresent
В новой версии Concurrent Hash Map для jdk 8 есть два новых метода.
computeIfAbsent
computeIfPresent
putIfAbsent - Старый метод
Я понимаю варианты использования putIfAbsent и computeIfAbsent.
Но я не уверен в сценариях, когда буду использовать computeIfPresent.
Также почему мне нужно putIfAbsent, если у меня есть computeIfPresent сейчас.
putIfAbsent действительно создает по крайней мере один дополнительный экземпляр значения.
Является ли причина только для обратной совместимости?
Ответы
Ответ 1
Как уже упоминалось в другом ответе: методы всегда будут сохраняться для обратной совместимости, даже если появились новые, более "мощные" методы.
Относительно варианта использования для computeIfPresent
: может быть трудно найти пример, который достаточно мал, чтобы не выглядеть надуманным и все еще быть убедительным. В общем, целью этого метода является обновление существующего значения в любой форме.
Одним из примеров может быть (ограниченное) количество слов. Для данного набора слов хранится начальный счет 0
на карте. Затем обрабатывается последовательность слов: всякий раз, когда вы находите слово из исходного набора, его счет увеличивается на 1:
import java.util.LinkedHashMap;
import java.util.Map;
public class ComputeIfPresentExample
{
public static void main(String[] args)
{
Map<String, Integer> wordCounts = new LinkedHashMap<String, Integer>();
String s =
"Lorem ipsum dolor sit amet consetetur iam nonumy sadipscing " +
"elitr, sed diam nonumy eirmod tempor invidunt ut erat sed " +
"labore et dolore magna dolor sit amet aliquyam erat sed diam";
wordCounts.put("sed", 0);
wordCounts.put("erat", 0);
for (String t : s.split(" "))
{
wordCounts.computeIfPresent(t, (k,v) -> v+1);
}
System.out.println(wordCounts);
}
}
(Конечно, такие вещи могут быть решены по-разному, но это довольно частая задача в той или иной форме, и новый метод позволяет довольно сжатое и элегантное решение)
Ответ 2
Распространенным вариантом использования являются карты с коллекциями, например
Map<String, Collection<String>> strings = new HashMap<>();
computeIfAbsent
и computeIfPresent
- очень удобные операции для добавления и удаления элементов в/из коллекции. Вот пример, который группирует строки по их первому символу. Обратите внимание, что ключи и коллекции создаются при необходимости, и очищаются, когда коллекция становится пустой:
void addString(String a) {
String index = a.substring(0, 1);
strings.computeIfAbsent(index, ign -> new HashSet<>()).add(a);
}
void removeString(String a) {
String index = a.substring(0, 1);
strings.computeIfPresent(index, (k, c) -> {
c.remove(a);
return c.isEmpty() ? null : c;
});
}
Пример:
// {}
addString("a1"); // {a=[a1]} <-- collection dynamically created
addString("a2"); // {a=[a1, a2]}
removeString("a1"); // {a=[a2]}
removeString("a2"); // {} <-- both key and collection removed
Это чрезвычайно эффективно в многопоточных средах, поскольку ConcurrentMaps
выполняют эти операции атомарно.
Операция удаления может быть однострочной:
void removeString(String a) {
String index = a.substring(0, 1);
strings.computeIfPresent(index, (i, c) -> c.remove(a) && c.isEmpty() ? null : c);
}
Ответ 3
JDK вряд ли когда-либо нарушает обратную совместимость. Потому что тогда вы не можете легко переносить или запускать программное обеспечение из старой версии с последней версией.
Вы можете запускать программное обеспечение, скомпилированное с более старой версией библиотеки, с любой версией (что означает, что у пользователей установлена JRE), которые все еще имеют эти функции.
Ответ 4
Я использовал computeIfPresent как нулевой способ для извлечения строчных значений из карты строк.
String s = fields.computeIfPresent("foo", (k,v) -> v.toLowerCase())
Прежде чем computeIfPresent был доступен, я должен был бы сделать это:
String s = map.get("foo");
if (s != null) {
s = s.toLowerCase();
}
Или это:
String s = map.containsKey("foo") ? map.get("foo").toLowerCase() : null;