累积值和 return 导致 Java 流

Accumulate values and return result in Java stream

我有一个包含 Seed 个元素的集合的 class。 Seed 方法的 return 类型之一是 Optional<Pair<Boolean, Boolean>>.

我正在尝试遍历所有 seeds,保持 return 类型 (Optional<Pair<Boolean, Boolean>>),但我想知道是否至少有 true 值(在任何 Pair 中)并用它覆盖结果。基本上,如果集合是(跳过 Optional 包装器以使事情更简单):[Pair<false, false>Pair<false, true>Pair<false, false>] 我想 return 和OptionalPair<false, true> 因为第二个元素有 true。最后,我感兴趣的是是否有 true 值,仅此而已。

  public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
    return seeds.stream()
        .map(Seed::hadExposure)
        ...
  }

我正在玩 reduce 但无法想出任何有用的东西。

My question is related with Java streams directly. I can easily do this with a for loop, but I aimed initially for streams.

您对 reduce 的想法看起来是正确的方法,使用 || 将每个 Pair 的两边一起减少。 (不确定你的 Optional 语义是什么,所以要在这里过滤掉空的语义,这可能会得到你想要的,但你可能需要调整):

Optional<Pair<Boolean, Boolean>> result = seeds.stream().map(Seed::hadExposure)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .reduce((a, b) -> new Pair<>(a.first || b.first, a.second || b.second));

因为你用 java-11 标记了这个问题,你可以使用 Optional.stream 方法:

public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
    return Optional.of(
        seeds.stream()
             .flatMap(seed -> seed.hadExposure().stream())
             .collect(
                 () -> new Pair<Boolean, Boolean>(false, false),
                 (p, seed) -> { 
                     p.setLeft(p.getLeft() || seed.getLeft());
                     p.setRight(p.getRight() || seed.getRight()); 
                 },
                 (p1, p2) -> {
                     p1.setLeft(p1.getLeft() || p2.getLeft());
                     p1.setRight(p1.getRight() || p2.getRight());
                 }));
}

这首先通过 Optional.stream 方法摆脱了 Optional (只保留对)然后使用 Stream.collect 可变 通过 OR associative 操作减少对。

注意:使用 Stream.reduce 也可以,但会产生很多不必要的中间对。这就是我使用 Stream.collect 的原因。

使用 Collectors.partitioningBy 你可以获得一个带有布尔键的地图,之后你可以轻松地检索使用键 true

索引的值
Optional<Pair<Boolean, Boolean>> collect = Arrays.asList(pair1, pair2, par3).stream()
            .filter(Optional::isPresent)
            .map(Optional::get)
            .collect(Collectors.collectingAndThen(Collectors.partitioningBy(p -> p.getFirst() == true || p.getSecond() == true),
                    m -> m.get(true).stream().findAny()));

直截了当

因为你是 Java 11 岁,你可以使用 Optional::stream (introduced in Java 9) 摆脱 Optional 包装。作为终端操作,reduce是你的朋友:

public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
    // wherever the seeds come from
    Stream<Optional<Pair<Boolean, Boolean>>> seeds = seeds();
    return seeds
        .flatMap(Optional::stream)
        .reduce((pair1, pair2) -> new Pair<>(
            pair1.left() || pair2.left(),
            pair1.right() || pair2.right())
    );
}

扩展

如果您想更进一步,为您的 Pair 提供一种与另一个 Pair 折叠成新实例的通用方法,您可以使代码更具表现力:

public class Pair<LEFT, RIGHT> {

    private final LEFT left;
    private final RIGHT right;

    // constructor, equals, hashCode, toString, ...

    public Pair<LEFT, RIGHT> fold(
            Pair<LEFT, RIGHT> other,
            BinaryOperator<LEFT> combineLeft,
            BinaryOperator<RIGHT> combineRight) {
        return new Pair<>(
            combineLeft.apply(left, other.left),
            combineRight.apply(right, other.right));
    }

}

// now you can use fold and Boolean::logicalOr
// https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Boolean.html#logicalOr(boolean,boolean)

public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
    Stream<Optional<Pair<Boolean, Boolean>>> seeds = seeds();
    return seeds
        .flatMap(Optional::stream)
        .reduce((pair1, pair2) -> pair1
            .fold(pair2, Boolean::logicalOr, Boolean::logicalOr))
    );
}

我可能不会只为这个用例创建 Pair::fold,但我会被诱惑。 ;)