问题描述
我有一个带有Seed
元素集合的类。方法的返回类型之一Seed
是Optional<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()));