Java 8 Map 中 putIfAbsent 和 computeIfAbsent 有什么区别?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/48183999/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 02:47:13  来源:igfitidea点击:

What is the difference between putIfAbsent and computeIfAbsent in Java 8 Map ?

javajava-8

提问by Adelin

Reading an interesting articles the guys claims that the difference between the two function are:

阅读一篇有趣的文章,这些人声称这两个功能之间的区别是:

Both functions aspire to add an element if the specified Key is not already present in Map.

putIfAbsent adds an element with the specified Value whereas computeIfAbsent adds an element with the value computed using the Key. http://www.buggybread.com/2014/10/java-8-difference-between-map.html

如果指定的 Key 不存在于 Map 中,这两个函数都希望添加一个元素。

putIfAbsent 添加具有指定值的元素,而 computeIfAbsent 添加具有使用 Key 计算的值的元素。 http://www.buggybread.com/2014/10/java-8-difference-between-map.html

And

We've seen that putIfAbsent removes the imperative way of having to define the if-statement, but what if fetching the Java articles is really hurting our performance?

To optimise this, we don't want to fetch the articles until we're really sure we need them — meaning we need to know if the key is absent before fetching the articles. http://www.deadcoderising.com/2017-02-14-java-8-declarative-ways-of-modifying-a-map-using-compute-merge-and-replace/

我们已经看到 putIfAbsent 消除了必须定义 if 语句的命令式方式,但是如果获取 Java 文章真的损害了我们的性能怎么办?

为了优化这一点,我们不想获取文章,直到我们真的确定需要它们——这意味着我们需要在获取文章之前知道键是否不存在。 http://www.deadcoderising.com/2017-02-14-java-8-declarative-ways-of-modifying-a-map-using-compute-merge-and-replace/

I didn't ready understand what are the differences can you please elaborate more on these two functions ?

我还没准备好了解有什么区别,请您详细说明这两个功能吗?

采纳答案by Andreas

Difference #1

差异#1

computeIfAbsenttakes a mapping function, that is called to obtain the value if the key is missing.

computeIfAbsent采用一个映射函数,如果键丢失,则调用该函数以获取值。

putIfAbsenttakes the value directly.

putIfAbsent直接取值。

If the value is expensive to obtain, then putIfAbsentwastes that if the key already exists.

如果该值的获取代价高昂,那么putIfAbsent如果该键已经存在,则浪费该值。

A common "expensive" value is e.g. new ArrayList<>()for when you're creating a Map<K, List<V>>, where creating a new list when the key already exists (which then discards the new list) generates unnecessary garbage.

一个常见的“昂贵”值是例如new ArrayList<>()当您创建 a 时Map<K, List<V>>,当键已经存在时创建一个新列表(然后丢弃新列表)会产生不必要的垃圾。



Difference #2

差异#2

computeIfAbsentreturns "the current (existing or computed) value associated with the specified key, or null if the computed value is null".

computeIfAbsent返回“与指定键关联的当前(现有或计算出的)值,如果计算出的值为空,则返回空值”。

putIfAbsentreturns "the previous value associated with the specified key, or null if there was no mapping for the key".

putIfAbsent返回“与指定键关联的前一个值,如果没有该键的映射,则返回 null”。

So, if the key already exists, they return the same thing, but if the key is missing, computeIfAbsentreturns the computed value, while putIfAbsentreturn null.

所以,如果键已经存在,它们返回相同的东西,但如果键丢失,则computeIfAbsent返回计算值,而putIfAbsent返回空值。



Difference #3

差异#3

Both method define "absent" as key missing or existing value is null, but:

这两种方法都将“不存在”定义为键缺失或现有值为空,但是:

computeIfAbsentwill not put a null value if the key is absent.

computeIfAbsent如果键不存在,则不会放置空值。

putIfAbsentwill put the value if the key is absent, even if the value is null.

putIfAbsent如果键不存在,即使该值为空,也会放置该值。

It makes no difference for future calls to computeIfAbsent, putIfAbsent, and getcalls, but it does make a difference to calls like getOrDefaultand containsKey.

这使得对未来没有什么区别调用computeIfAbsentputIfAbsent以及get调用,但它确实像呼叫差异getOrDefaultcontainsKey

回答by Eran

Suppose you have a Map<String,ValueClass>.

假设你有一个Map<String,ValueClass>.

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

will create a ValueClassinstance anyway, even if the "key" key is already in the Map. This would just create an unnecessary instance.

ValueClass无论如何都会创建一个实例,即使“key”键已经在Map. 这只会创建一个不必要的实例。

On the other hand

另一方面

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

will only create a ValueClassinstance if the "key" key is not already in the Map(or is mapped to a nullvalue).

ValueClass如果“键”键不在Map(或映射到null值)中,则只会创建一个实例。

Therefore computeIfAbsentis more efficient.

因此computeIfAbsent效率更高。

putIfAbsentis equivalent to:

putIfAbsent相当于:

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

while computeIfAbsentis equivalent to:

whilecomputeIfAbsent相当于:

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

Another small difference between the two methods is that computeIfAbsentwill not put a nullvalue for an absent key. putIfAbsentwill.

两种方法之间的另一个小区别是computeIfAbsent不会null为不存在的键放置值。putIfAbsent将要。

回答by Jesper

You can understand the difference by carefully looking at the method signatures:

您可以通过仔细查看方法签名来理解差异:

  • putIfAbsenttakes a key and value, and puts the value in the map if there is no value for that key in the map.
  • computeIfAbsenttakes a key and a Function. If there is no value for that key in the map, the function is called to create the value, which is then put in the map.
  • putIfAbsent接受一个键和值,如果映射中没有该键的值,则将该值放入映射中。
  • computeIfAbsent需要一把钥匙和一个Function. 如果映射中没有该键的值,则调用该函数来创建该值,然后将该值放入映射中。

If you already have the value, use putIfAbsent.

如果您已经拥有该值,请使用putIfAbsent.

If you don't have the value yet and creating the value is an expensive operation (for example, the value has to be looked up in a database), then use computeIfAbsent, so that the expensive operation doesn't need to be performed in case the map already contains a value for the specified key.

如果您还没有该值并且创建该值是一项代价高昂的操作(例如,必须在数据库中查找该值),则使用computeIfAbsent,这样就不需要执行代价高昂的操作,以防万一映射已包含指定键的值。

回答by minotna

V putIfAbsent(K key, V value)- If the specified key is not already associated with a value (or is mapped to null), attempts to compute its value using the given mapping function and enters it into this map unless null.

V putIfAbsent(K key, V value)- 如果指定的键尚未与值关联(或映射为 null),则尝试使用给定的映射函数计算其值并将其输入此映射,除非为 null。

V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)- If the specified key is not already associated with a value (or is mapped to null), attempts to compute its value using the given mapping function and enters it into this map unless null.

V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)- 如果指定的键尚未与值关联(或映射为 null),则尝试使用给定的映射函数计算其值并将其输入此映射,除非为 null。

Reading documentation can give you more obvious answer. https://docs.oracle.com/javase/8/docs/api/java/util/Map.html

阅读文档可以给你更明显的答案。 https://docs.oracle.com/javase/8/docs/api/java/util/Map.html

回答by zlakad

Maybe the default implementations can clarify a little bit more....

也许默认实现可以澄清一点....

default V putIfAbsent?(K key, V value)The default implementation is equivalent to, for this map:

default V putIfAbsent?(K key, V value)对于此映射,默认实现等效于:

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

On the other hand:

另一方面:

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

is equivalent to:

相当于:

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

回答by Jain

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.

回答by S Kumar

This question has already been answered. I took a little time to understand ("expensive operation doesn't need to be performed in case the map already contains a value for the specified key in computeIfAbsent") I am putting my understanding here. Hope this will be helpful to others:

这个问题已经有了答案。我花了一点时间来理解(“如果映射已经包含computeIfAbsent 中指定键的值,则不需要执行昂贵的操作”)我将我的理解放在这里。希望这对其他人有帮助:

putIfAbsent() behaves simply as put() when map already contains the value for the specified key. putIfAbsent() only checks whether key is null or not. If it is not null then it returns the value and then map fetch that value again.

当 map 已包含指定键的值时, putIfAbsent() 的行为与 put() 类似。putIfAbsent() 只检查 key 是否为空。如果它不为空,则返回该值,然后 map 再次获取该值。

However, in computeIfAbsent() there is null check for both key and value. During null check for value, if it is not null then existing value from the map object is assigned to newValue and it is returned. That's why there is no need to again fetch the value as existing value from map is re-used.

但是,在computeIfAbsent() 中,键和值都进行了空检查。在空值检查期间,如果它不是空值,则将地图对象中的现有值分配给 newValue 并返回。这就是为什么不需要再次获取该值的原因,因为 map 中的现有值已被重新使用。

Refer to following program for reference:

参考以下程序:

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;
    }
}

Refer to following code for putIfAbsent() and computeIfAbsent() from interface Map.class

接口 Map.class 中的 putIfAbsent() 和 computeIfAbsent() 参考以下代码

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;
    }   
.........
}