如何使用新的 computeIfAbsent 函数?

2022-08-31 07:31:23

我非常想使用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);
}

新方法:

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

但在他们的例子中,我认为我并不完全“明白”。如何转换代码以使用新的 lambda 方式来表达这一点?


答案 1

最近我也在玩这种方法。我写了一个记忆算法来计算斐波那契数列,这可以作为如何使用该方法的另一个例证。

我们可以从定义一个映射开始,并将基本情况的值放入其中,即: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)
}

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

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

如您所见,当数字不存在于映射中时,该方法将使用提供的 lambda 表达式来计算斐波那契数列。这是对传统树递归算法的重大改进。computeIfAbsent


答案 2

假设您有以下代码:

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();
    }
}

然后,您将在第二次调用时看到该消息一次,因为该密钥已经有一个值。lambda 表达式中的 只是映射将传递给 lambda 以计算值的键的放置(参数)。因此,在示例中,密钥被传递给函数调用。creating a value for "snoop"computeIfAbsentkk -> f(k)

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

为了更接近问题中的示例,您可以将其编写为(无论是否命名参数或 )。或者把它写成好像是或 if 是一个实例方法。whoLetDogsOut.computeIfAbsent("snoop", key -> tryToLetOut(key));kkeywhoLetDogsOut.computeIfAbsent("snoop", MyClass::tryToLetOut);tryToLetOutstaticwhoLetDogsOut.computeIfAbsent("snoop", this::tryToLetOut);tryToLetOut