如何使用新的computeIfAbsent函数?

Ben*_*n H 98 java lambda dictionary java-8

我非常想使用Map.computeIfAbsent,但是因为lambdas在本科课程中已经太长了.

几乎直接来自文档:它给出了一个旧方法的例子:

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);
}
Run Code Online (Sandbox Code Playgroud)

而新的方式:

map.computeIfAbsent(key, k -> new Value(f(k)));
Run Code Online (Sandbox Code Playgroud)

但在他们的例子中,我认为我并没有"得到它".我如何转换代码以使用新的lambda表达方式?

Edw*_*rzo 105

最近我也玩这种方法.我写了一个memoized算法来计算Fibonacci数,这可以作为如何使用该方法的另一个例子.

我们可以从定义一个地图并将其中的值放入基本情况开始,即:fibonnaci(0)fibonacci(1):

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

对于归纳步​​骤,我们所要做的就是重新定义我们的Fibonacci函数,如下所示:

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

如您所见,computeIfAbsent当数字不在地图中时,该方法将使用提供的lambda表达式计算Fibonacci数.这代表了对传统的树递归算法的显着改进.

  • 文档字面上说*映射函数不应该在计算*期间修改这个映射,所以这个答案显然是错误的. (15认同)
  • 很好,单行转换为动态编程.非常光滑. (14认同)
  • 这个代码导致`HashMap`的内部被破坏,就像在https://bugs.openjdk.java.net/browse/JDK-8172951中一样,并且在Java 9中会出现`ConcurrentModificationException`(https:// bugs. openjdk.java.net/browse/JDK-8071667) (8认同)
  • 递归使用computeIfAbsent时应该更加谨慎.欲了解更多详情请http://stackoverflow.com/questions/28840047/recursive-concurrenthashmap-computeifabsent-call-never-terminates-bug-or-fea (7认同)
  • +1,我喜欢这个Fibonacci版本:D (6认同)
  • 如果您先进行(n-2)呼叫,可能会减少递归呼叫次数? (2认同)

Hol*_*ger 90

假设您有以下代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您将看到该消息creating a value for "snoop"正好一次,因为在第二次调用时computeIfAbsent已经存在该键的值.的k在λ表达式k -> f(k)仅仅是该地图将传递到您的拉姆达用于计算值的键一个placeolder(参数).因此,在示例中,键被传递给函数调用.

或者你可以编写:whoLetDogsOut.computeIfAbsent("snoop", k -> k.isEmpty());在没有辅助方法的情况下实现相同的结果(但是你不会看到调试输出).甚至更简单,因为它是对现有方法的简单委托,您可以编写:whoLetDogsOut.computeIfAbsent("snoop", String::isEmpty);此委托不需要编写任何参数.

要在你的问题更接近例如,你可以把它写成whoLetDogsOut.computeIfAbsent("snoop", key -> tryToLetOut(key));(这不要紧,你是否命名参数kkey).或者将其写为whoLetDogsOut.computeIfAbsent("snoop", MyClass::tryToLetOut);if tryToLetOutis staticwhoLetDogsOut.computeIfAbsent("snoop", this::tryToLetOut);if tryToLetOut是实例方法.


hex*_*abc 35

另一个例子.构建复杂的地图地图时,computeIfAbsent()方法可替代地图的get()方法.通过将computeIfAbsent()调用链接在一起,可以通过提供的lambda表达式即时构建丢失的容器:

  // 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");
Run Code Online (Sandbox Code Playgroud)


nan*_*itv 21

如果您想在不使用guava库的情况下创建Multimap,这非常有用(https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html)

例如:如果您要存储为特定主题注册的学生列表.使用jdk库的常规解决方案是

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

//continue....
Run Code Online (Sandbox Code Playgroud)

由于它有一些锅炉板代码,人们倾向于使用番石榴Mutltimap.

使用Map.computeIfAbsent,我们可以在没有番石榴Multimap的单行中编写如下.

studentListSubjectWise.computeIfAbsent("a", (x -> new ArrayList<>())).add("John");
Run Code Online (Sandbox Code Playgroud)

Stuart Marks和Brian Goetz对这个https://www.youtube.com/watch?v=9uTVXxJjuco进行了很好的讨论