如何使用新的computeIfAbsent函数?

时间:2013-10-09 17:10:01

标签: java dictionary lambda 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);
}

新的方式:

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

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

5 个答案:

答案 0 :(得分:92)

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

我们可以从定义地图开始,并为基本情况添加值,即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)
}

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

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

正如您所看到的,方法computeIfAbsent将使用提供的lambda表达式来计算地图中不存在数字时的Fibonacci数。这代表了对传统树递归算法的重大改进。

答案 1 :(得分:81)

假设您有以下代码:

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

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

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

要更接近问题中的示例,您可以将其写为whoLetDogsOut.computeIfAbsent("snoop", key -> tryToLetOut(key));(无论您是将参数命名为k还是key)都无关紧要。如果whoLetDogsOut.computeIfAbsent("snoop", MyClass::tryToLetOut);tryToLetOut,则将其写为static,如果whoLetDogsOut.computeIfAbsent("snoop", this::tryToLetOut);是实例方法,则将其写为tryToLetOut

答案 2 :(得分:31)

另一个例子。构建复杂的地图地图时,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");

答案 3 :(得分:18)

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

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

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

//continue....

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

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

studentListSubjectWise.computeIfAbsent("a", (x -> new ArrayList<>())).add("John");
斯图尔特马克斯&amp; Brian Goetz对此进行了很好的讨论 https://www.youtube.com/watch?v=9uTVXxJjuco

答案 4 :(得分:0)

想出了这个比较示例(旧与新),它演示了两种方法;

static Map<String, Set<String>> playerSkills = new HashMap<>();
public static void main(String[] args) {
    //desired output
    //player1, cricket, baseball
    //player2, swimming

    //old way
    add("Player1","cricket");
    add("Player2","swimming");
    add("Player1","baseball");
    
    System.out.println(playerSkills);

    //clear
    playerSkills.clear();
    
    //new
    addNew("Player1","cricket");
    addNew("Player2","swimming");
    addNew("Player1","baseball");
    System.out.println(playerSkills);
    
}

private static void add(String name, String skill) {
    Set<String> skills = playerSkills.get(name);
    if(skills==null) {
        skills= new HashSet<>();
        playerSkills.put(name, skills);
    }
    skills.add(skill);
}

private static void addNew(String name, String skill) {
    playerSkills
            .computeIfAbsent(name, set -> new HashSet<>())
            .add(skill);
}