ConcurrentHashMap JDK 8, когда использовать calculateIfPresent

В новой версии Concurrent Hash Map jdk 8 есть два новых метода.

вычислить, если отсутствует

computeIfPresent

putIfAbsent — старый метод

Я понимаю варианты использования putIfAbsent и computeIfAbsent. Но я не уверен в сценариях, когда я буду использовать computeIfPresent. Также зачем мне нужно putIfAbsent, если у меня сейчас есть calculateIfPresent. putIfAbsent создайте хотя бы один дополнительный экземпляр значения.

Причина только в обратной совместимости?


person veritas    schedule 21.07.2014    source источник


Ответы (4)


Как упоминалось в другом ответе: методы всегда будут сохраняться для обратной совместимости, даже если будут введены новые, более «мощные» методы.

Относительно варианта использования для 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);
    }
}

(Конечно, подобные вещи можно было бы решить и по-другому, но это довольно частая задача в том или ином виде, и новый метод допускает достаточно лаконичное и изящное решение)

person Marco13    schedule 21.07.2014
comment
Еще одно преимущество computeIfPresent: параметр значения для BiFunction гарантированно не равен нулю. - person aksh1618; 27.01.2020

Распространенным вариантом использования являются карты с < href="https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html" rel="nofollow noreferrer">коллекции, например

Map<String, Collection<String>> strings = new HashMap<>();

computeIfAbsent и computeIfPresent — очень удобные операции для добавления и удаления элементов в/из коллекции. Хотя бы потому, что, в отличие от put(), методы compute*() возвращают текущее значение (независимо от того, было оно только что создано или нет). Вот пример группировки строк по их первому символу. Обратите внимание, что и ключи, и коллекции создаются при необходимости и очищаются, когда коллекция становится пустой:

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< /a> выполнять эти операции атомарно.

Операция удаления может быть однострочной:

void removeString(String a) {
    String index = a.substring(0, 1);
    strings.computeIfPresent(index, (i, c) -> c.remove(a) && c.isEmpty() ? null : c);
}

Итак, еще раз вкратце:

Map<String, Set<String>> map = new ConcurrentHashMap<>();
map.computeIfAbsent(key, i -> ConcurrentHashMap.newKeySet()).add(value);
map.computeIfPresent(key, (i, s) -> s.remove(value) && s.isEmpty() ? null : s);
person steffen    schedule 01.09.2016
comment
Это реальный вариант использования computeIfPresent. - person john16384; 28.03.2019

JDK почти никогда не нарушает обратную совместимость. Потому что тогда вы не сможете легко портировать или запускать программное обеспечение из более старой версии с последней версией.

Вы можете запускать программное обеспечение, скомпилированное с более старой версией библиотеки, с любой версией (имеются в виду пользователи, у которых установлена ​​JRE), в которой все еще есть эти функции.

person Thirler    schedule 21.07.2014
comment
@thriller о, я знаю, что это должна быть обратная совместимость. мой вопрос это единственная причина? а также вариант использования calculateIfPresent. - person veritas; 21.07.2014

Я использовал 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;
person austin327    schedule 29.04.2016
comment
Значение, возвращаемое computeIfPresent, будет введено в карту, поэтому, когда вы получите ключ таким образом, он изменит карту и сделает запись строчной... Я не думаю, что это то, что вам нужно. . - person john16384; 28.03.2019