对Java8中的HashMap感到困惑

时间:2016-10-22 20:21:32

标签: java hashmap java-8

我有一个hashmap,它将String和HashSet作为键和值。 我正在尝试更新地图并在其中添加值。

我无法理解使用以下哪种方法 -

  1. map.putIfAbsent(str.substring(i,j),new HashSet<String>).add(str); //this method gives nullpointerexception

  2. map.computeIfPresent(str.substring(i,j),(k,v)->v).add(str);

  3. 在输出中,我可以看到相同的键被添加两次,带有初始值和更新值。

    有人请告诉我如何使用这些方法。

2 个答案:

答案 0 :(得分:3)

最好的方法是使用Map#computeIfAbsent。这样就不会不必要地创建新的HashSet,并且之后会返回该值。

map.computeIfAbsent(str.substring(i, j), k -> new HashSet<>()).add(str);

答案 1 :(得分:2)

没有理由在putIfAbsentcomputeIfPresent之间进行选择。最值得注意的是,computeIfPresent完全不合适,正如其名称所暗示的那样,只有在已经存在旧值时才计算新值,并且(k,v)->v甚至使该计算成为无操作。

有几个选项

  1. containsKeyputget。这是最受欢迎的Java 8之前版本,虽然它是此列表中效率最低的,因为它为同一个密钥最多包含三个哈希查找

    String key=str.substring(i, j);
    if(!map.containsKey(key))
        map.put(key, new HashSet<>());
    map.get(key).add(str);
    
  2. getput。比第一个好,但它仍然可以包含两个查找。对于普通的Map,这是Java 8之前的最佳选择:

    String key=str.substring(i, j);
    Set<String> set=map.get(key);
    if(set==null)
        map.put(key, set=new HashSet<>());
    set.add(str);
    
  3. putIfAbsent。在Java 8之前,此选项仅适用于ConcurrentMap

    String key=str.substring(i, j);
    Set<String> set=new HashSet<>(), old=map.putIfAbsent(key, set);
    (old!=null? old: set).add(str);
    

    这只有一个哈希查找,但需要无条件创建新的HashSet,即使我们不需要它。在这里,首先执行get可能值得推迟创建,尤其是在使用ConcurrentMap时,因为get可以无锁执行,并且可能使后续更加昂贵putIfAbsent不必要。

    另一方面,必须强调的是,这个构造不是线程安全的,因为值Set的操纵不受任何东西的保护。

  4. computeIfAbsent。这种Java 8方法允许最简洁和最有效的操作:

    map.computeIfAbsent(str.substring(i, j), k -> new HashSet<>()).add(str);
    

    这只会评估函数,如果没有旧值,并且与putIfAbsent不同,此方法返回新值,如果没有旧值,换句话说,它返回右{{1在任何一种情况下,我们都可以直接Set。尽管如此,add操作仍在add操作之外执行,因此即使Map是线程安全的,也没有线程安全性。但对于普通Map s,即如果线程安全性不是问题,这是最有效的变体。

  5. Map。这个Java 8方法将始终评估该函数,并可以以两种方式使用。第一个

    compute

    只是map.compute(str.substring(i, j), (k,v) -> v==null? new HashSet<>(): v).add(str); 的一个更详细的变体。第二个

    computeIfAbsent

    将在map.compute(str.substring(i, j), (k,v) -> { if(v==null) v=new HashSet<>(); v.add(str); return v; }); 的线程安全策略下执行Set更新,因此在Map的情况下,这将是线程安全更新,因此使用ConcurrentHashMap当线程安全性成为问题时,而不是compute具有有效的用例。