How do I use the new computeIfAbsent function?

匿名 (未验证) 提交于 2019-12-03 02:45:02

问题:

I very much want to use Map.computeIfAbsent but it has been too long since lambdas in undergrad.

Almost directly from the docs: it gives an example of the old way to do things:

Map<String, Boolean> whoLetDogsOut = new ConcurrentHashMap<>(); String key = "snoop"; if (whoLetDogsOut.get(key) == null) {   Boolean isLetOut = tryToLetOut(key);   if (isLetOut != null)     map.putIfAbsent(key, isLetOut); } 

And the new way:

map.computeIfAbsent(key, k -> new Value(f(k))); 

But in their example, I think I'm not quite "getting it." How would I transform the code to use the new lambda way of expressing this?

回答1:

Suppose you have the following code:

import java.util.Map; import java.util.concurrent.ConcurrentHashMap;  public class Test {     public static void main(String[] s) {         Map<String, Boolean> whoLetDogsOut = new ConcurrentHashMap<>();         whoLetDogsOut.computeIfAbsent("snoop", k -> f(k));         whoLetDogsOut.computeIfAbsent("snoop", k -> f(k));     }     static boolean f(String s) {         System.out.println("creating a value for \""+s+'"');         return s.isEmpty();     } } 

Then you will see the message creating a value for "snoop" exactly once as on the second invocation of computeIfAbsent there is already a value for that key. The k in the lambda expression k -> f(k) is just a placeolder (parameter) for the key which the map will pass to your lambda for computing the value. So in the example the key is passed to the function invocation.

Alternatively you could write: whoLetDogsOut.computeIfAbsent("snoop", k -> k.isEmpty()); to achieve the same result without a helper method (but you won’t see the debugging output then). And even simpler, as it is a simple delegation to an existing method you could write: whoLetDogsOut.computeIfAbsent("snoop", String::isEmpty); This delegation does not need any parameters to be written.

To be closer to the example in your question, you could write it as whoLetDogsOut.computeIfAbsent("snoop", key -> tryToLetOut(key)); (it doesn’t matter whether you name the parameter k or key). Or write it as whoLetDogsOut.computeIfAbsent("snoop", MyClass::tryToLetOut); if tryToLetOut is static or whoLetDogsOut.computeIfAbsent("snoop", this::tryToLetOut); if tryToLetOut is an instance method.



回答2:

Recently I was playing with this method too. I wrote a memoized algorithm to calcualte Fibonacci numbers which could serve as another illustration on how to use the method.

We can start by defining a map and putting the values in it for the base cases, namely, fibonnaci(0) and fibonacci(1):

private static Map<Integer,Long> memo = new HashMap<>(); static {    memo.put(0,0L); //fibonacci(0)    memo.put(1,1L); //fibonacci(1) } 

And for the inductive step all we have to do is redefine our Fibonacci function as follows:

public static long fibonacci(int x) {    return memo.computeIfAbsent(x, n -> fibonacci(n-2) + fibonacci(n-1)); } 

As you can see, the method computeIfAbsent will use the provided lambda expression to calculate the Fibonacci number when the number is not present in the map. This represents a significant improvement over the traditional, tree recursive algorithm.



回答3:

Another example. When building a complex map of maps, the computeIfAbsent() method is a replacement for map's get() method. Through chaining of computeIfAbsent() calls together, missing containers are constructed on-the-fly by provided lambda expressions:

  // Stores regional movie ratings   Map<String, Map<Integer, Set<String>>> regionalMovieRatings = new TreeMap<>();    // This will throw NullPointerException!   regionalMovieRatings.get("New York").get(5).add("Boyhood");    // This will work   regionalMovieRatings     .computeIfAbsent("New York", region -> new TreeMap<>())     .computeIfAbsent(5, rating -> new TreeSet<>())     .add("Boyhood"); 


回答4:

This is really helpful if you want to create a Multimap without using guava library (https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html)

For eg: If you want to store a list of students who enrolled for a particular subject. The normal solution for this using jdk library is

Map<String,List<String>> studentListSubjectWise = new TreeMap<>(); List<String>lis = studentListSubjectWise.get("a"); if(lis == null) {     lis = new ArrayList<>(); } lis.add("John");  //continue.... 

Since it have some boiler plate code, people tend to use guava Mutltimap.

Using Map.computeIfAbsent, we can write in a single line without guava Multimap as follows.

studentListSubjectWise.computeIfAbsent("a", (x -> new ArrayList<>())).add("John"); 

Stuart Marks & Brian Goetz did a good talk about this https://www.youtube.com/watch?v=9uTVXxJjuco



回答5:

There is no difference between using computeIfAbsent() and simple put() get()
functions of a map. In other words you can rewrite your function in this way

for (char ch : input){             Integer value;             if(countMap.containsKey(ch)){                 value = countMap.get(ch);                 value++;                 countMap.put(ch, value);             }             else{                 value = 1;                 countMap.put(ch, value);              }         } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!