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


77

他们读了一篇有趣的文章,声称这两个功能之间的区别是:

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

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

我们已经看到putIfAbsent消除了必须定义if语句的必要方法,但是如果获取Java文章确实会损害我们的性能呢?

为了优化这一点,我们在真正确定需要它们之前不希望获取文章-这意味着我们需要在获取文章之前先了解密钥是否缺失。 http://www.deadcoderising.com/2017-02-14-java-8-declarative-ways-of-modifying-a-map-using-compute-merge-and-replace/

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


1
有或没有。如果您拥有价值,则可以投入使用;如果没有,则必须先计算然后放入。
laune

1
看到这个stackoverflow.com/questions/10743622/…,看起来也像一个副本
Eugene,

Answers:


143

差异#1

computeIfAbsent 采用映射函数,如果缺少键,则调用该函数以获取值。

putIfAbsent 直接取值。

如果获取该值很昂贵,那么putIfAbsent如果密钥已经存在,那就浪费了。

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


差异#2

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

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

因此,如果键已经存在,则它们返回相同的内容,但是如果键丢失,则computeIfAbsent返回计算值,而putIfAbsent返回null。


差异#3

两种方法都将“缺少”定义为缺少键或现有值为null,但是:

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

putIfAbsent 即使键为空,也将放置键的值。

以后对computeIfAbsentputIfAbsent和的get调用没有区别,但对像getOrDefault和的调用却有区别containsKey


4
您刚刚保存了我的一天!谢谢。
franta kocourek

4
不同的返回语义非常令人困惑,这就是我最终在这里阅读您非常有帮助的答案的方式。
Wojtek '18

#2的差异导致了一些NPE,因为我return map.putIfAbsent("key", someMethodReturningNotNullObj);fn函数内部进行了尝试,并且想知道为什么我从fn函数中获取空值。computeIfAbsent就是这样!
Silviu Burcea

返回旧值会使putIfAbsent()与put()一致,后者也会返回旧值。
reggoodwin

@reggoodwinputIfAbsent与不一致put,因为保留putIfAbsent现有值不变,而put替换该值,因此方法名称的“如果不存在”部分。
安德里亚斯

62

假设您有一个Map<String,ValueClass>

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

ValueClass即使“ key”键已经在中,它仍然会创建一个实例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);
}

whilecomputeIfAbsent等效于:

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

两种方法之间的另一个小区别是computeIfAbsent不会null为缺少的键设置值。putIfAbsent将。


7
一个非常频繁的用于calculateIfAbsent的用途是Map<X,List<Y>>,您必须在第一次遇到键值时创建一个列表。最初是进行获取,测试,更新,放置...现在只是computeIfAbsent(..., new ArrayList())
laune

7

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

  • putIfAbsent 接受一个键和一个值,如果该键在映射中没有值,则将该值放入映射中。
  • computeIfAbsent需要一个钥匙和一个Function。如果映射中没有该键的值,则调用该函数以创建值,然后将其放入映射中。

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

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


6

也许默认的实现可以澄清更多……。

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

1

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

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

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


0

以以下简单示例为例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"

cx3如果3地图中尚无Value,则将为value 。


问题是有关的区别putIfAbsent()computeIfAbsent,不只是什么putIfAbsent()呢。
沃尔夫森

我想你混淆了keyvalue这里。请参阅Class HashMap<K,V>
沃尔夫森

0

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

putIfAbsent()行为就像put()地图已经包含指定键的值时一样。putIfAbsent()仅检查key是否为null。如果它不为null,则返回该值,然后map再次获取该值。

但是,computeIfAbsent()对于键和值都没有null检查。在对值进行null检查期间,如果它不为null,则将来自映射对象的现有值分配给newValue并将其返回。这就是为什么不需要重新获取值的原因,因为重复使用了map中的现有值。

请参考以下程序以供参考:

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;
    }   
.........
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.