如何在Java 8中使用方法参考进行Map合并?

时间:2018-09-20 17:36:01

标签: dictionary merge java-8 collectors

我有以下两种调用收集操作的形式,它们都返回相同的结果,但是我仍然不能完全依赖于方法引用并且需要一个lambda。

<R> R collect(Supplier<R> supplier,
          BiConsumer<R,? super T> accumulator,
          BiConsumer<R,R> combiner)

为此,请考虑以下由100个随机数组成的流

List<Double> dataList = new Random().doubles().limit(100).boxed()
            .collect(Collectors.toList());

1)以下示例使用纯lambdas

Map<Boolean, Integer> partition = dataList.stream()
            .collect(() -> new ConcurrentHashMap<Boolean, Integer>(),
(map, x) ->
{
    map.merge(x < 0.5 ? Boolean.TRUE : Boolean.FALSE, 1, Integer::sum);
}, (map, map2) ->
{
    map2.putAll(map);
});

2)以下尝试使用方法引用,但第二个参数仍需要一个lambda

Map<Boolean, Integer> partition2 = dataList.stream()
            .collect(ConcurrentHashMap<Boolean, Integer>::new, 
(map, x) ->
{
    map.merge(x < 0.5 ? Boolean.TRUE : Boolean.FALSE, 1, Integer::sum);
}, Map::putAll);

在本例中,如何重写Java 8中collect方法的第二个参数以使用方法引用而不是lambda?

System.out.println(partition.toString());
System.out.println(partition2.toString());
{false=55, true=45}
{false=55, true=45}

1 个答案:

答案 0 :(得分:3)

如果您有一个完全可以完成预期工作的现有方法,则方法参考是一种方便的工具。如果您需要改编或其他操作,则方法引用没有特殊语法来支持该语法,除非您认为lambda表达式就是该语法。

当然,您可以在您的类中创建一个新方法来执行所需的操作并创建对该方法的引用,这是代码复杂度提高时的正确方法,因为这样,它将获得一个有意义的名称并可以测试。但是对于简单的代码片段,您可以使用lambda表达式,这只是实现相同结果的一种简单语法。从技术上讲,没有区别,只是编译器生成的包含lambda表达式主体的方法将被标记为“合成”。

在您的示例中,您甚至不能使用Map::putAll作为合并函数,因为那样会覆盖第一张地图的所有现有映射,而不是合并值。

正确的实现方式看起来像

Map<Boolean, Integer> partition2 = dataList.stream()
    .collect(HashMap::new, 
             (map, x) -> map.merge(x < 0.5, 1, Integer::sum),
             (m1, m2) -> m2.forEach((k, v) -> m1.merge(k, v, Integer::sum)));

,但您无需自己实施。 Collectors类中已经提供了适当的内置收集器:

Map<Boolean, Long> partition2 = dataList.stream()
    .collect(Collectors.partitioningBy(x -> x < 0.5, Collectors.counting()));