ConcurrentHashMap putIfAbsent() возвращает ноль

Следующая программа печатает null. Я не могу понять, почему.

public class ConcurrentHashMapTest {
    public static final ConcurrentMap<String, String> map = new ConcurrentHashMap<>(5, 0.9f, 2);

    public static void main(String[] args) {
        map.putIfAbsent("key 1", "value 1");
        map.putIfAbsent("key 2", "value 2");

        String value = get("key 3");
        System.out.println("value for key 3 --> " + value);
    }

    private static String get(final String key) {
        return map.putIfAbsent(key, "value 3");
    }
}

Может ли кто-нибудь помочь мне понять поведение?


person Niranjan    schedule 07.02.2014    source источник
comment
Какого поведения вы ожидали и почему?   -  person Jon Skeet    schedule 07.02.2014


Ответы (9)


ConcurrentMap.putIfAbsent возвращает предыдущее значение, связанное с указанным ключом, или null, если для ключа не было сопоставления. У вас не было значения, связанного с ключом 3. Все правильно.

ПРИМЕЧАНИЕ. Это относится не только к ConcurrentMap, но и ко всем реализациям Map.

person Evgeniy Dorofeev    schedule 07.02.2014

Проблема в том, что по определению putIfAbsent возвращает старое значение, а не новое значение (старое значение для отсутствия всегда равно нулю). Используйте computeIfAbsent — это вернет вам новое значение.

private static String get(final String key) {
    return map.computeIfAbsent(key, s -> "value 3");
}
person Ondřej Stašek    schedule 03.01.2018
comment
это не позволяет генерировать исключения, очень плохо. - person Jerry Chin; 28.09.2018
comment
Как и любая другая лямбда. Поэтому либо используйте @SneakyThrows, либо перегенерируйте исключение как RuntimeException. - person Ondřej Stašek; 29.09.2018
comment
@SneakyThrows зло. - person Frans; 01.06.2020
comment
проверенные исключения гораздо злее, чем @SneakyThrows :) - person Ondřej Stašek; 02.06.2020


Это часто задаваемый вопрос, который, возможно, предполагает, что такое поведение не интуитивно понятно. Возможно, путаница возникает из-за того, как dict.setdefault() работает в python и других языках. Возврат того же объекта, который вы только что поместили, помогает сократить несколько строк кода.

Рассмотреть возможность:

if (map.contains(value)){
    obj = map.get(key);
}
else{
    obj = new Object();
}

против:

obj = map.putIfAbsent(key, new Object());
person Jose H. Martinez    schedule 30.06.2015
comment
Это атомарная операция? Например, может ли карта меняться между contains и get? - person nurettin; 27.12.2018

Это в javadoc:

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

person Bozho    schedule 07.02.2014
comment
Для кавычек следует использовать >, а не форматирование кода. - person Mark Rotteveel; 07.02.2014
comment
Ага, забыл... :) давно не отвечал - person Bozho; 07.02.2014

Прочтите документацию по ConcurrentHashMap.putIfAbsent:

Возвращает:
предыдущее значение, связанное с указанным ключом, или null, если для ключа не было сопоставления.

Поскольку предыдущего значения для ключа "key 3" не было, возвращается null.

person Mark Rotteveel    schedule 07.02.2014

Если вы посмотрите на документацию, там написано

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

В вашем случае с ключом ранее не было связано значение, поэтому NULL

person Ankur Shanbhag    schedule 07.02.2014

Текущее сопоставленное значение может быть возвращено с помощью функции merge. Следующее может вернуть текущее ненулевое значение, если ключ уже существует, или вернуть заданное новое значение, если сопоставление не существует или если значение равно null.

private static String get(final String key) {
    return map.merge(key, "value 3", (oldVal, newVal) -> oldVal);
}

или вообще:

private T get(final String key, T value) {
    return map.merge(key, value, (oldVal, newVal) -> oldVal);
}

Это может быть полезно, если вы не предпочитаете использовать computeIfAbsent, потому что функция сопоставления с computeIfAbsent может вызвать исключение, и вы также не хотите делать следующее:

map.putIfAbsent(key, value);
return map.get(key);
person Gautham M    schedule 19.05.2021

Все ответы правильные, и просто добавить примечание,

Если указанный ключ еще не связан со значением (или сопоставлен с нулем), он связывает его с заданным значением и возвращает значение null, иначе возвращает текущее значение.

public V putIfAbsent (ключ K, значение V) { return putVal (ключ, значение, истина); }

Ключ сохраняется в таблице. Значение можно получить, вызвав метод get с ключом, равным исходному ключу перед помещением. Если ключ не найден в таблице, возвращает null.

person prostý člověk    schedule 01.06.2021