[英]Confused about HashMap in Java8
我有一個哈希圖,將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);
在輸出中,我可以看到同一鍵被兩次添加了初始值和更新值。
有人請告訴我如何使用這些方法。
最好的方法是使用Map#computeIfAbsent
。 這樣,就不必不必要地創建新的HashSet
,它將在之后返回該值。
map.computeIfAbsent(str.substring(i, j), k -> new HashSet<>()).add(str);
沒有理由在putIfAbsent
和computeIfPresent
之間進行選擇。 最值得注意的是, 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
不同,此方法將返回新值,如果沒有舊值,換句話說,無論哪種情況,它都會返回正確的Set
,因此我們可以直接add
對此。 盡管如此, add
操作還是在Map
操作之外執行的,因此即使Map
是線程安全的,也沒有線程安全。 但是對於普通Map
,即如果不考慮線程安全性,則這是最有效的變體。
compute
。 此Java 8方法將始終評估函數,並且可以通過兩種方式使用。 第一個
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; });
將在Map
的線程安全策略下執行Set
更新,因此在ConcurrentHashMap
情況下,這將是線程安全的更新,因此,當考慮到線程安全時,使用compute
而不是computeIfAbsent
具有有效的用例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.