2017-08-02 52 views
11

Java8介紹那些好的方法getOrDefault()putIfAbsent(),讓寫代碼,如:我應該在使用getOrDefault()之後使用put()或putIfAbsent()嗎?

Map<Foo, List<Bar>> itemsByFoo = ... 
List<Bar> bars = itemsByFoo.getOrDefault(key, new ArrayList<>()); 
bars.add(someNewBar); 

現在我如果有好的事實理由要麼不知道做:

itemsByFoo.put(key, bars); 

itemsByFoo.putIfAbsent(key, bars); 

兩者都可以工作:

  • 選項1可能做了很多不必要的「放」呼叫時將元素添加到列表經常發生
  • 選項2可能做了很多不必要的「的containsKey」的呼籲時添加新條目,新密鑰是主導

SO:是選擇1還是選項2「總是」的好理由?

+13

Ahem,*既不*。對於整個操作,使用'itemsByFoo.computeIfAbsent(key,x - > new ArrayList <>()).add(someNeBar);'。 – Holger

+0

@Holger是:)極好的一點。因爲'putIfAbsent'可能會返回'null',因爲它返回* previous *值...此外'computeifAbsent'存在於java-8中,而不是7個。我之前遇到過這種情況...... – Eugene

+2

@Eugene:'putIfAbsent'被添加到Java 8的'Map'接口中,因爲現在可以使用'default'方法,但它必須保留自Java 5以來存在的'ConcurrentMap.putIfAbsent'的協定,所以它不像'computeIfAbsent'那樣方便...... – Holger

回答

19

getOrDefault適用於如果您想在不修改地圖的情況下使用缺省值的替身。如果您想爲缺席鍵添加新值,則可以在一個操作中正確執行。

List<Bar> bars = itemsByFoo.computeIfAbsent(key, x -> new ArrayList<>()); 
bars.add(someNewBar); 

甚至

itemsByFoo.computeIfAbsent(key, x -> new ArrayList<>()).add(someNewBar); 

在最好的情況下,當由Map實施被覆蓋,像HashMap,這將只承擔一個哈希查找。

不是說putIfAbsent只有在使用default實現時纔會出現兩次查找,但當然,大多數Map實現將爲其提供單個查找實現。儘管如此,getOrDefaultputIfAbsent的組合仍然會在最佳情況下進行兩次查找,而經過優化的computeIfAbsent只能執行一次。

+0

不是'itemsFoo.compute ...; bars.add(someNewBar)現在是一種競爭狀態?因爲它不是一個單一的原子操作;有人可以在「add」完成時刪除該條目?我想知道我今天是否在耐心的限制中遇到如此多的以下問題... – Eugene

+5

@Eugene:這是一個普通的'Map'問題。原子是不是一個要求。否則,你還有很多事情要做。雖然你可以使插入線程安全地完成'compute'中的所有操作,但它與最終讀取'List'的代碼沒有任何關係,並且必須有代碼讀取它,如果存儲本身不是目的,那麼對於任何現實生活中的情況,無論如何都需要額外的努力。 – Holger

+0

謝謝。我在這裏看到'CHM'在哪裏?我的錯。對於'putIfAbsent'來說,這是一個很好的觀點,它在默認實現中執行了2次查找......我注意到它現在只是「get」,然後是「put」。這將是一個很好的例子,說明爲什麼默認方法被覆蓋。 – Eugene

5

computeIfAbsent重要的一點是,它需要將只能得到執行,如果Key不存在一個Function,我們需要一個默認Value

getOrDefault需要默認的Value本身已計算。在這種情況下,我們需要的默認Valuenew ArrayList<Bar>(),它具有在堆上分配新對象的副作用。

我們希望推遲這樣做,直到我們確信key尚未在itemsByFoo中。否則我們會爲gc收集產生不必要的垃圾。

+0

優秀的附加信息;-) – GhostCat