我有一个hashmap,它将String和HashSet作为键和值。 我正在尝试更新地图并在其中添加值。
我无法理解使用以下哪种方法 -
map.putIfAbsent(str.substring(i,j),new HashSet<String>).add(str);
//this method gives nullpointerexception
map.computeIfPresent(str.substring(i,j),(k,v)->v).add(str);
在输出中,我可以看到相同的键被添加两次,带有初始值和更新值。
有人请告诉我如何使用这些方法。
答案 0 :(得分:3)
最好的方法是使用Map#computeIfAbsent
。这样就不会不必要地创建新的HashSet
,并且之后会返回该值。
map.computeIfAbsent(str.substring(i, j), k -> new HashSet<>()).add(str);
答案 1 :(得分:2)
没有理由在putIfAbsent
和computeIfPresent
之间进行选择。最值得注意的是,computeIfPresent
完全不合适,正如其名称所暗示的那样,只有在已经存在旧值时才计算新值,并且(k,v)->v
甚至使该计算成为无操作。
有几个选项
containsKey
,put
和get
。这是最受欢迎的Java 8之前版本,虽然它是此列表中效率最低的,因为它为同一个密钥最多包含三个哈希查找
String key=str.substring(i, j);
if(!map.containsKey(key))
map.put(key, new HashSet<>());
map.get(key).add(str);
get
和put
。比第一个好,但它仍然可以包含两个查找。对于普通的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);
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
的操纵不受任何东西的保护。
computeIfAbsent
。这种Java 8方法允许最简洁和最有效的操作:
map.computeIfAbsent(str.substring(i, j), k -> new HashSet<>()).add(str);
这只会评估函数,如果没有旧值,并且与putIfAbsent
不同,此方法返回新值,如果没有旧值,换句话说,它返回右{{1在任何一种情况下,我们都可以直接Set
。尽管如此,add
操作仍在add
操作之外执行,因此即使Map
是线程安全的,也没有线程安全性。但对于普通Map
s,即如果线程安全性不是问题,这是最有效的变体。
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
具有有效的用例。