В чем разница между putIfAbsent и computeIfAbsent в Java 8 Map?

Читая интересные статьи, ребята утверждают, что разница между двумя функциями:

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

putIfAbsent добавляет элемент с указанным значением, тогда как computeIfAbsent добавляет элемент со значением, вычисленным с помощью ключа. http://www.buggybread.com/2014/10/java-8-difference-between-map.html

И

Мы видели, что putIfAbsent устраняет императивный способ иметь определить if-statement, но что, если выборка статей Java действительно вредит нашей работе?

Чтобы оптимизировать это, мы не хотим получать статьи до тех пор, пока действительно уверены, что они нам нужны - это означает, что нам нужно знать, является ли ключ отсутствует перед тем, как выучить статьи. http://www.deadcoderising.com/2017-02-14-java-8-declarative-ways-of-modifying-a-map-using-compute-merge-and-replace/

Я не был готов понять, в чем отличия, вы можете подробнее рассказать об этих двух функциях?

Ответ 1

Разница # 1

computeIfAbsent принимает функцию сопоставления, которая вызывается для получения значения, если ключ отсутствует.

putIfAbsent принимает значение напрямую.

Если значение дорогого получить, то putIfAbsent уничтожает это, если ключ уже существует.

Общим "дорогостоящим" значением является, например, new ArrayList<>(), когда вы создаете Map<K, List<V>>, где создание нового списка, когда ключ уже существует (который затем отбрасывает новый список), генерирует ненужный мусор.


Разница # 2

computeIfAbsent возвращает "текущее (существующее или вычисленное) значение, связанное с указанным ключом, или null, если вычисленное значение равно null".

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

Итак, если ключ уже существует, они возвращают одно и то же, но если ключ отсутствует, computeIfAbsent возвращает вычисленное значение, а putIfAbsent возвращает null.


Разница № 3

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

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

putIfAbsent поместит значение, если ключ отсутствует, даже если значение равно null.

Не имеет значения для будущих вызовов computeIfAbsent, putIfAbsent и get, но это действительно имеет значение для вызовов типа getOrDefault и containsKey.

Ответ 2

Предположим, что у вас есть Map<String,ValueClass>.

map.putIfAbsent("key", new ValueClass());

создаст экземпляр ValueClass в любом случае, даже если ключ "ключ" уже находится в Map. Это просто создаст ненужный экземпляр.

С другой стороны,

map.computeIfAbsent("key", k -> new ValueClass());

создаст только экземпляр ValueClass, если ключ "ключ" еще не находится в Map (или отображается на значение null).

Поэтому computeIfAbsent более эффективен.

putIfAbsent эквивалентно:

ValueClass value = new ValueClass();
if (map.get("key") == null) {
    map.put("key",value);
}

а computeIfAbsent эквивалентно:

if (map.get("key") == null) {
    map.put("key",new ValueClass());
}

Еще одна небольшая разница между двумя методами заключается в том, что computeIfAbsent не будет помещать значение null для отсутствующего ключа. putIfAbsent будет.

Ответ 3

Вы можете понять разницу, внимательно изучив сигнатуры метода:

  • putIfAbsent принимает ключ и значение и помещает значение в карту, если для этой клавиши нет значения.
  • computeIfAbsent берет ключ и Function. Если для этого ключа на карте нет значения, функция вызывается для создания значения, которое затем помещается на карту.

Если у вас уже есть значение, используйте putIfAbsent.

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

Ответ 4

Может быть, реализация по умолчанию может прояснить немного больше....

default V putIfAbsent​(K key, V value) Реализация по умолчанию эквивалентна, для этой карты:

 V v = map.get(key);
  if (v == null)
      v = map.put(key, value);
  return v;

С другой стороны:

default V computeIfAbsent​(K key,
                          Function<? super K,? extends V> mappingFunction)

эквивалентно:

if (map.get(key) == null) {
     V newValue = mappingFunction.apply(key);
     if (newValue != null)
         map.put(key, newValue);
}

Ответ 5

V putIfAbsent(K key, V value) - Если указанный ключ еще не связан со значением (или сопоставлен с нулем), пытается вычислить его значение с помощью данной функции отображения и ввести ее в эту карту, если не указано значение null.

V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) - Если указанный ключ еще не связан со значением (или сопоставлен с нулем), пытается вычислить его значение с использованием данной функции отображения и ввести ее в эту карту, если не указано значение null.

Документация для чтения может дать вам более очевидный ответ. https://docs.oracle.com/javase/8/docs/api/java/util/Map.html

Ответ 6

На этот вопрос уже был дан ответ. Я потратил немного времени, чтобы понять ("дорогая операция не должна выполняться, если карта уже содержит значение для указанного ключа в computeIfAbsent") Я выражаю свое понимание здесь. Надеюсь, что это будет полезно для других:

putIfAbsent() ведет себя просто как put(), когда map уже содержит значение для указанного ключа. putIfAbsent() только проверяет, является ли ключ нулевым или нет. Если он не равен нулю, он возвращает значение, а затем снова выбирает это значение.

Однако в computeIfAbsent() есть нулевая проверка как ключа, так и значения. Во время нулевой проверки значения, если оно не равно нулю, то существующее значение из объекта карты присваивается newValue и возвращается. Поэтому нет необходимости снова извлекать значение, так как существующее значение из карты используется повторно.

Обратитесь к следующей программе для справки:

public class MapTest1 {
    public static final String AJAY_DEVGAN = "Ajay Devgn";
    public static final String AUTOBIOGRAPHY = "Autobiography";

    public static void main(String[] args) {
        MapTest1 mt = new MapTest1();
        mt.testPutCompute();
    }

    private void testPutCompute() {
        Map<String, List<String>> movies = getMovieObject();
        System.out.println("\nCalling putIfAbsent method.....");
        //System.out.println(movies.get(AJAY_DEVGAN));
        //movies.put(AJAY_DEVGAN, getAjayDevgnMovies());
        movies.putIfAbsent(AJAY_DEVGAN, getAjayDevgnMovies());

        System.out.println("\nCalling computeIfAbsent method......");
        //System.out.println(movies.get(AUTOBIOGRAPHY));
        movies.computeIfAbsent(AUTOBIOGRAPHY, t -> getAutobiographyMovies());

    }

    private Map<String, List<String>> getMovieObject() {
        Map<String, List<String>> movies = new HashMap<>();     

        movies.put(AJAY_DEVGAN, getAjayDevgnMovies());
        movies.put(AUTOBIOGRAPHY, getAutobiographyMovies());

        System.out.println(movies);
        return movies;
    }

    private List<String> getAutobiographyMovies() {
        System.out.println("Getting autobiography movies");
        List<String> list = new ArrayList<>();
        list.add("M.S. Dhoni - The Untold Story");
        list.add("Sachin: A Billion Dreams");
        return list;
    }

    private List<String> getAjayDevgnMovies() {
        System.out.println("Getting Ajay Devgn Movies");
        List<String> ajayDevgnMovies = new ArrayList<>();
        ajayDevgnMovies.add("Jigar");
        ajayDevgnMovies.add("Pyar To Hona Hi Tha");
        return ajayDevgnMovies;
    }
}

Смотрите следующий код для putIfAbsent() и computeIfAbsent() из интерфейса Map.class

public interface Map<K,V> {
.....

 default V putIfAbsent(K key, V value) {
        V v = get(key);
        if (v == null) {
            v = put(key, value);
        }

        return v;
    }

 default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v;
        if ((v = get(key)) == null) {
            V newValue;
            if ((newValue = mappingFunction.apply(key)) != null) {
                put(key, newValue);
                return newValue;
            }
        }

        return v;
    }   
.........
}

Ответ 7

Take this simple Example for putIfAbsent():
Map myMap = new HashMap();
myMap.put(1,"ABC");
myMap.put(2,"XYZ");
myMap.put(3,"GHI");
//Output of map till this point is : 1-> "ABC", 2-> "XYZ", 3-> "GHI"
myMap.putIfAbsent(3,"cx");
//Output of map till this point is : 1-> "ABC", 2-> "XYZ", 3-> "GHI"
cx will be value of 3 if there is no Value of 3 in the map already.