【问题标题】:How do I use the new computeIfAbsent function?如何使用新的 computeIfAbsent 函数?
【发布时间】:2013-10-17 04:34:27
【问题描述】:

我非常想使用Map.computeIfAbsent,但是自从本科生使用 lambda 以来已经太久了。

几乎直接来自文档:它提供了一个旧方法的示例:

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 方式来表达这一点?

【问题讨论】:

  • 我不确定您从那里的示例中不明白什么?
  • 什么是“k”?它是一个被定义的变量吗? “新值”怎么样 - 是来自 java 8 的东西,还是代表我需要定义或覆盖的对象? whoLetDogsOut.computeIfAbsent(key, k -> new Boolean(tryToLetOut(k))) 无法编译,所以我遗漏了一些东西......
  • 究竟什么不能编译?它会产生什么错误?
  • Temp.java:26: 错误:非法开始表达式 whoLetDogsOut.computeIfAbsent(key, k -> new Boolean(tryToLetOut(k))); (指向“>”)
  • 对我来说编译得很好。确保您真正使用 Java 8 编译器。其他 Java 8 功能是否有效?

标签: java dictionary lambda java-8


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    多地图

    如果您想创建 multimap 而不借助 Google Guava 库来实现 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....
    

    由于它有一些样板代码,人们倾向于使用 Guava Mutltimap

    使用 Map.computeIfAbsent,我们可以在没有 guava Multimap 的情况下单行编写如下。

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

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

    【讨论】:

    • 在 Java 8 中创建多映射的另一种方法(更简洁)是只做 studentListSubjectWise.stream().collect(Collectors.GroupingBy(subj::getSubjName, Collectors.toList()); 这会在 JDK 中生成一个类型为 Map&lt;T,List&lt;T&gt; 的多映射,恕我直言。
    • 添加到答案中;关于computeIfAbsent的部分从25:30开始
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      【解决方案5】:

      假设你有以下代码:

      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 -&gt; f(k) 中的 k 只是映射将传递给您的 lambda 以计算值的键的占位符(参数)。因此,在示例中,密钥被传递给函数调用。

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

      为了更接近您问题中的示例,您可以将其写为whoLetDogsOut.computeIfAbsent("snoop", key -&gt; tryToLetOut(key));(将参数命名为kkey 都没有关系)。或者如果tryToLetOutstaticwhoLetDogsOut.computeIfAbsent("snoop", this::tryToLetOut); 如果tryToLetOut 是实例方法,则将其写为whoLetDogsOut.computeIfAbsent("snoop", MyClass::tryToLetOut);

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-07-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-12-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多