9

对于我的具体情况,我想在减少中使用功能组合;例如:

BiFunction<ImmutableSet<Integer>, ImmutableSet<Integer>, Sets.SetView<Integer>> f = Sets::intersection;
Function<Sets.SetView<Integer>, ImmutableSet<Integer>> g = Sets.SetView::immutableCopy;
BiFunction<ImmutableSet<Integer>, ImmutableSet<Integer>, ImmutableSet<Integer>> biFunction = f.andThen(g);
ImmutableSet<Integer> intersection = Stream.of(ImmutableSet.of(1, 2, 3), ImmutableSet.of(1, 2), ImmutableSet.of(4))
    .reduce(biFunction)
    .orElse(ImmutableSet.of());

这有一个编译错误:

argument mismatch BiFunction cannot be converted to BinaryOperator

相反,我需要这样做:

ImmutableSet<Integer> intersection = Stream.of(ImmutableSet.of(1, 2, 3), ImmutableSet.of(1, 2), ImmutableSet.of(4))
    .reduce((a, b) -> Sets.intersection(a, b).immutableCopy())
    .orElse(ImmutableSet.of());

然而,这失去了组合提供的无点风格。

为什么 Stream API 是这样设计的?ABinaryOperator是 a ,所以用超类型BiFunction声明方法的参数不是更有意义吗?reduce

4

4 回答 4

5

reduce 操作必须采用相同类型的参数并返回相同类型。如果没有,就会出现类型不匹配。就是这样BinaryOperatorBinaryOperator<T> extends BiFunction<T,T,T>

您可以创建自己的BiFunction. 然后创建一个BinaryOperator

import java.util.function.BinaryOperator;
import java.util.function.BiFunction;
import java.util.function.Function;

import java.util.stream.Stream;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;

public class StackOverflowTest {
  public static void main(String[] args) {

    BiFunction<ImmutableSet<Integer>, ImmutableSet<Integer>, Sets.SetView<Integer>> f = Sets::intersection;
    Function<Sets.SetView<Integer>, ImmutableSet<Integer>> g = Sets.SetView::immutableCopy;

    BiFunction<ImmutableSet<Integer>, ImmutableSet<Integer>, ImmutableSet<Integer>> biFunction = f.andThen(g);

    BinaryOperator<ImmutableSet<Integer>> biOperator = biFunction::apply;

    ImmutableSet<Integer> intersection =
       Stream.of(ImmutableSet.of(1, 2, 3),
                 ImmutableSet.of(1, 2),
                 ImmutableSet.of(1, 4)) // added a 1
             .reduce(biOperator)
             .orElse(ImmutableSet.of());

    System.out.println(intersection);

/*
prints:
[1]
*/
  }
}
于 2020-02-18T00:57:30.117 回答
2

我认为您的实际问题可能不会有令人满意的答案;我能想到的将更严格的类型BinaryOperator<T>作为参数的唯一好处是可读性,但这可能会或可能不会出现在 API 设计者的脑海中。除非做出决定的人之一写下答案,否则我们可能永远不会知道做出决定的理由是什么。

也就是说,在您的特定情况下,该Sets.SetView::immutableCopy函数似乎在缩减中是不必要的,因为该Sets::intersection函数不需要其参数是 type ImmutableSet<E>,只要我们指定流值是较弱的 type Set<E>。所以以下在逻辑上应该是等价的:

ImmutableSet<Integer> intersection = 
  Stream.<Set<Integer>>of(/* the sets */)
        .reduce(Sets::intersection)
        .map(ImmutableSet::copyOf)
        .orElse(ImmutableSet.of());

由于Sets::intersection返回视图而不进行任何复制,因此可能存在性能差异。如果相交可能相对于原始集合很大,并且集合的数量不大,那么这个版本应该更有效,因为它减少了内存分配和复制。否则,如果交叉点可能很小,或者集合的数量很大,那么复制可能是有益的,因为迭代较小的集合比查看两个大集合的交叉点更快。

也就是说,在第二种情况下,我建议用for循环的命令式编写它,这样如果累加器已经为空,你可以提前停止。

于 2020-02-18T13:43:08.540 回答
0

可能是为了清楚地说明减少不应该是累积的意图(参见减少操作),并且必须是操作而不是函数(太笼统)。作为一种操作通常被认为是按价值操作?

纳曼的回答也是一个很好的暗示,因为reduce(biFunction::apply)可以很容易地申请。

于 2020-02-18T08:23:05.113 回答
0

这要简单得多,并且与现有的重载相关

Optional<T> reduce​(BinaryOperator<T> accumulator)

在问题中使用,可用于:

ImmutableSet<Integer> intersection = Stream.of(ImmutableSet.of(1, 2, 3), ImmutableSet.of(1, 2), ImmutableSet.of(4))
        .reduce(biFunction::apply)
        .orElse(ImmutableSet.of());
于 2020-02-18T04:44:11.403 回答