为什么map.merge()每次都不调用重映射函数?

Nil*_*esh 5 java lambda hashmap java-8

Map.merge()javadoc说,

如果指定的键尚未与值关联或与null关联,则将其与给定的非空值关联.否则,将相关值替换为给定重映射函数的结果,或者如果结果为null则删除.当组合密钥的多个映射值时,该方法可以是有用的.例如,要创建或附加String msg到值映射.

例如,要计算一篮子中每种类型的水果数量,代码如下:

public static void main(String[] args) {
    Map<String, Integer> fruitCounts = new HashMap<>();
    List<String> fruitBasket = Arrays.asList(
        "Apple", "Banana", "Apple", "Orange", "Mango", "Orange", "Mango", "Mango");
    for (String fruit : fruitBasket) {
        fruitCounts.merge(fruit, 1/*First fruit of this type*/, (k, v) -> v + 1);
    }
    System.out.println(fruitCounts);
}
Run Code Online (Sandbox Code Playgroud)

有2个苹果,3个芒果,2个橙子和1个香蕉,但实际产量是

{Apple=2, Mango=2, Orange=2, Banana=1}
Run Code Online (Sandbox Code Playgroud)

这段代码有什么问题?

Mri*_*jay 7

问题出在这里

(k, v) -> v + 1
Run Code Online (Sandbox Code Playgroud)

你应该这样做

(k, v) -> k + v
Run Code Online (Sandbox Code Playgroud)

如果检查合并的实现,则表示remappingFunction.apply(oldValue, value);现有值将是第一个参数,您应在其中添加初始化它的相同数字,作为该函数的第二个参数.

更新

  • 请注意,`(k,v) - > k + v`也可以表示为`Integer :: sum`,但这是品味问题...... (4认同)
  • 在您的情况下,@Nilesh新值始终是`merge`函数的第二个参数。 (2认同)
  • `(k,v) - > k + v`和`Integer :: sum`将具有完全相同的性能.使用`Integer :: sum`时,类文件可能会略短. (2认同)

Tav*_*avo 5

完成@Mritunjay答案,这是等效的用法compute,您可以在其中看到差异:

fruitCounts.compute(fruit, (k,v) -> v == null ? 1 : v + 1) //computing over the value
fruitCounts.merge(fruit, 1, (oldValue, newValue) -> oldValue + 1) //merging over the value
Run Code Online (Sandbox Code Playgroud)

  • 当然。关键是它不是键,而是地图中已经存在的值。使用`(k,v)-&gt;`是令人困惑的。相反,如果您使用的是((oldValue,newValue)-&gt;`,那么一切将会更加清晰。 (2认同)
  • @Federico Peralta Schaffner:公平地说,JRE开发人员在实现`Collectors.toMap(…)`时(犯了同样的错误)(https://bugs.openjdk.java.net/browse/JDK-8040892)没有合并功能),将旧值报告为重复键。 (2认同)