是否可以检查数组(或集合)是否包含除5之外的元素5 和元素。在 one 流中返回布尔结果而不是使用两个流:
int[] ints = new int[]{1, 2, 3, 4, 5};
boolean hasFive = IntStream.of(ints).anyMatch(num -> num == 5);
boolean hasNonFive = IntStream.of(ints).anyMatch(num -> num != 5);
boolean result = hasFive && hasNonFive;
答案 0 :(得分:6)
在这种特定情况下,即你想知道一个流或数组是否包含匹配和非匹配元素(与谓词的否定相匹配的元素),你可以更简单地做到这一点。
首先,测试第一个元素是否与谓词或其否定匹配,然后,搜索流是否包含任何相反的匹配:
IntPredicate predicate=i -> i==5;
if(ints.length>0 && predicate.test(ints[0]))
predicate=predicate.negate();
boolean result = IntStream.of(ints).anyMatch(predicate);
就是这样。如果您没有数组或集合作为流源,而是任意流,则测试第一个元素有点棘手:
IntPredicate[] tmp={ null };
Spliterator.OfInt sp=intStream.spliterator();
boolean result = sp.tryAdvance(
(int i) -> tmp[0]=predicate.test(i)? predicate.negate(): predicate)
&& StreamSupport.intStream(sp, false).anyMatch(tmp[0]);
答案 1 :(得分:6)
这是涉及我的StreamEx库的两个解决方案。我在这里使用的核心功能是短路收集器的概念。我的库增强了Collector
概念,以提供短路功能(适用于顺序和并行流)
如果谓词与您的样本中的一样(一个与另一个相反),您可以使用partitioningBy
:
Map<Boolean, Optional<Integer>> map = IntStreamEx.of(ints).boxed()
.partitioningBy(num -> num == 5, MoreCollectors.first());
现在您应该检查两个映射是否存在:
System.out.println(map.values().stream().allMatch(Optional::isPresent));
或单一声明:
System.out.println(IntStreamEx.of(ints).boxed()
.partitioningBy(num -> num == 5, MoreCollectors.first())
.values().stream().allMatch(Optional::isPresent));
这里我们使用MoreCollectors.first()
短路收集器。此解决方案类似于@ user140547提出的解决方案,但只要找到这两个元素,它就会立即停止处理。
对于两个自定义谓词,可以使用pairing
收集器,它结合了两个收集器的结果(如果输入收集器是短路的,则保留短路)。但首先,我们需要anyMatching
收集器(我的库中没有):
import static one.util.streamex.MoreCollectors.*;
static <T> Collector<T, ?, Boolean> anyMatching(Predicate<T> pred) {
return collectingAndThen(filtering(pred, first()), Optional::isPresent);
}
Collector<Integer, ?, Boolean> hasFive = anyMatching(num -> num == 5);
Collector<Integer, ?, Boolean> hasNonFive = anyMatching(num -> num != 5);
Collector<Integer, ?, Boolean> hasBoth = pairing(hasFive, hasNonFive,
(res1, res2) -> res1 && res2);
System.out.println(IntStreamEx.of(ints).boxed().collect(hasBoth));
答案 2 :(得分:5)
我认为如何做到这一点的一种方法是从多个IntPredicate
创建自定义IntPredicate
。每次测试一个值时,我们都会尝试从该数组中找到与之匹配的谓词,如果匹配,我们将其存储在内部Set
内(以正确处理重复项)。当存储集与初始数组具有相同的大小时,表示所有谓词都已匹配,我们的自定义谓词可以返回true
。
我的初始解决方案使用Set<Integer>
来存储匹配的谓词的索引。正如@Holger评论的那样,使用BitSet
并存储不匹配谓词的索引可能更有效。
private static class MultipleIntPredicate implements IntPredicate {
private IntPredicate[] predicates;
private BitSet unmatchedPredicates;
public MultipleIntPredicate(IntPredicate... predicates) {
this.predicates = predicates;
unmatchedPredicates = new BitSet(predicates.length);
unmatchedPredicates.set(0, predicates.length, true); // initially, all predicates are unmatched
}
@Override
public boolean test(int value) {
unmatchedPredicates.stream()
.filter(i -> predicates[i].test(value))
.findFirst()
.ifPresent(unmatchedPredicates::clear); // when a match is found, clear the BitSet
return unmatchedPredicates.isEmpty(); // return true if all the predicates were matched
}
}
像这样使用它:
int[] ints = new int[] {1, 2, 3, 4, 5};
MultipleIntPredicate predicate = new MultipleIntPredicate(num -> num == 5, num -> num != 5);
boolean hasFiveAndNonFive = IntStream.of(ints).anyMatch(predicate);
System.out.println(hasFiveAndNonFive);
对于数组的情况,就像你的问题一样,这个解决方案可能比在数组上迭代两次更有开销。但是,在无限IntStream
的情况下,此谓词仍然可以正常工作。它还具有以下优点:所需谓词不必与其自身相反。
答案 3 :(得分:1)
如果您不介意使用盒装流并且2个谓词就足够了,您可以使用Collectors.partitioningBy
并执行以下操作:
Map<Boolean, List<Integer>> collect = IntStream.of(ints).boxed().collect(Collectors.partitioningBy(x -> x == 5));
boolean hasFive = !collect.get(true).isEmpty();
boolean hasNonFive = !collect.get(false).isEmpty();
另一个解决方案(对于多个谓词)可能不像Tunaki的解决方案那样高效并且可能创建了太多的数组,但是没有使用可变的BitSet
......
Boolean[] result = IntStream.of(ints).mapToObj(i ->
new Boolean[]{four.test(i), five.test(i), six.test(i)}
).reduce(new Boolean[]{false, false, false}, Test::or);