【问题标题】:What is the difference between putIfAbsent and computeIfAbsent in Java 8 Map ?Java 8 Map 中的 putIfAbsent 和 computeIfAbsent 有什么区别?
【发布时间】:2018-06-19 10:06:43
【问题描述】:

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

如果指定的 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/

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

【问题讨论】:

标签: java java-8


【解决方案1】:

区别 #1

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

putIfAbsent 直接取值。

如果获取该值的成本很高,那么如果密钥已经存在,putIfAbsent 就会浪费它。

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


区别 #2

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

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

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


区别 #3

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

如果键不存在,computeIfAbsent 不会输入空值。

putIfAbsent 将在 key 不存在时放入 value,即使 value 为 null。

这对于以后调用 computeIfAbsentputIfAbsentget 没有影响,但对于像 getOrDefaultcontainsKey 这样的调用确实有影响。

【讨论】:

  • #2 差异导致了一些 NPE,因为我在 fn 函数中尝试了 return map.putIfAbsent("key", someMethodReturningNotNullObj);,我想知道为什么我从 fn 函数中得到空值。 computeIfAbsent 就是这样!
  • 返回旧值使得putIfAbsent()与put()一致,也返回旧值。
  • @reggoodwin putIfAbsentput 不一致,因为putIfAbsent 保持现有值不变,而put 替换该值,因此方法名称的“如果不存在”部分.
  • 你好,我想我的意思是这两种方法都不会返回传递给它们的新值。谢谢你的回答。
【解决方案2】:

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

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

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

另一方面

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

仅当“key”键不在Map 中(或映射到null 值)时才会创建ValueClass 实例。

因此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会。

【讨论】:

  • computeIfAbsent 的一个相当频繁的用途是Map<X,List<Y>>,您必须在第一次遇到键值时创建一个列表。最初一个人做了一个 get、test、new、put... 现在它只是 computeIfAbsent(..., new ArrayList())
【解决方案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】:

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

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

      但是,在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;
          }
      }
      

      从接口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】:

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

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

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

        【讨论】:

          【解决方案7】:

          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"
          

          如果地图中没有3 的值,cx 的值将是3

          【讨论】:

          • 问题是关于putIfAbsent()computeIfAbsent 之间的区别,而不仅仅是putIfAbsent() 的作用。
          • 我认为你在这里混淆了keyvalue。见Class HashMap&lt;K,V&gt;
          猜你喜欢
          • 1970-01-01
          • 2014-12-28
          • 1970-01-01
          • 1970-01-01
          • 2014-11-12
          • 2015-01-23
          • 2016-12-24
          • 2023-03-04
          相关资源
          最近更新 更多