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

时间:2018-01-10 09:21:02

标签: java java-8

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

  

如果指定的Key不是,则两个函数都希望添加元素   已存在于地图中。

     

putIfAbsent添加具有指定值的元素   computeIfAbsent添加一个元素,其值使用Key计算得出。   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/

我还没有准备好了解哪些差异可以请你详细说明这两个功能?

7 个答案:

答案 0 :(得分:78)

差异#1

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

putIfAbsent直接获取该值。

如果获取的值很昂贵,那么如果密钥已经存在,则putIfAbsent会浪费。

常见且价格昂贵的#34;价值是例如new ArrayList<>()当您创建Map<K, List<V>>时,如果密钥已存在(然后丢弃新列表),则创建新列表会产生不必要的垃圾。

差异<2

computeIfAbsent返回&#34;与指定键关联的当前(现有或计算)值,如果计算值为null,则返回null&#34;。

putIfAbsent返回&#34;与指定键关联的前一个值,如果没有键的映射&#34;则返回null。

因此,如果密钥已经存在,则返回相同的内容,但如果缺少密钥,computeIfAbsent将返回计算值,而putIfAbsent将返回null。

差异#3

这两种方法都定义了#34;缺席&#34;密钥缺失或现有值为null,但是:

如果密钥不存在,

computeIfAbsent将不会设置空值。

如果密钥不存在,

putIfAbsent将输入值,即使该值为空。

computeIfAbsentputIfAbsentget来电的未来通话没有任何区别,但它确实对getOrDefault和{{3}等通话产生了影响}。

答案 1 :(得分:40)

假设您有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会。

答案 2 :(得分:3)

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

  • putIfAbsent获取键和值,如果地图中没有该键的值,则将值放入地图中。
  • computeIfAbsent获取密钥和Function。如果地图中没有该键的值,则调用该函数创建该值,然后将其放入地图中。

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

如果您还没有该值并且创建该值是一项昂贵的操作(例如,必须在数据库中查找该值),则使用computeIfAbsent,以便昂贵的操作不会如果地图已经包含指定键的值,则需要执行。

答案 3 :(得分: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

答案 4 :(得分:1)

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

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 :(得分: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;
    }
}

请从接口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;
    }   
.........
}

答案 6 :(得分:-1)

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.