累积值并在Java流中返回结果

问题描述

我有一个带有Seed元素集合的类。方法的返回类型之一SeedOptional<Pair<Boolean,Boolean>>

我试图遍历所有seeds,保留返回类型(Optional<Pair<Boolean,Boolean>>),但是我想说一下是否至少有true值( Pair中的任何一个),并使用它覆盖结果。基本上,如果集合是(跳过Optional包装器使事情更简单):[Pair<false,false>Pair<false,true>Pair<false,false>]我想返回,{{1} Optional中的},因为第二个元素具有Pair<false,true>。最后,我很想知道是否有一个true值,就是这个值。

true

我当时在玩 public Optional<Pair<Boolean,Boolean>> hadAnyExposure() { return seeds.stream() .map(Seed::hadExposure) ... } ,但无法提出任何有用的信息。

我的问题与Java流直接相关。我可以使用reduce循环轻松地做到这一点,但最初的目标是流。

解决方法

向前

由于您使用的是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,pair2) -> pair1
            .fold(pair2,Boolean::logicalOr,Boolean::logicalOr))
    );
}

我可能不会仅为此用例创建Pair::fold,但我会很受诱惑。 ;)

,

您对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方法(仅保留对)来摆脱Optional.stream,然后使用Stream.collect可变地减少对通过OR associative 操作。

注意:使用Stream.reduce也可以,但是会创建很多不必要的中间对。这就是为什么我改用Stream.collect的原因。

,

使用Collectors.partitioningBy可以获取带有布尔键的Map,之后您可以轻松检索使用键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()));