是否有可能检查所有Java 8流元素是否满足给定谓词之一?

时间:2016-08-11 18:37:13

标签: java java-8 java-stream

使用流API,我可以使用allMatch(e -> predicate(e))方法轻松检查所有元素是否满足给定条件。我还可以检查是否满足任何多个条件allMatch(e -> predicateA(e) || predicateB(e) || predicateC(e))。但是有可能检查所有元素是否满足其中一个谓词(任一个)?在前一种情况下,某些元素可能满足A而其中一些元素不满足,但它们满足B或C(反之亦然)。

我可以多次执行allMatch,但之后流将被终止,我需要重复初步的。

我还可以设计一个棘手的reduce操作,但是当结果显然是假的时候(就像allMatch方法那样),它就无法提前停止。

2 个答案:

答案 0 :(得分:4)

这是一种可能的方法,可以追溯到使用简单的Iterator而不是Stream的元素(因此它没有并行支持,但适用于任何类型和任意数量的谓词)

它创建一个具有给定谓词大小的初始BitSet'将所有位设置为true的长度,并且每次检索下一个元素时,我们清除(设置为false)不匹配的谓词的索引。因此,在每个索引处,位集将包含到目前为止该谓词是否都与流的元素匹配。它是短路的,因为它循环直到有剩余的元素并且位集不为空(意味着仍有谓词所有匹配到目前为止所考虑的元素)。

@SafeVarargs
private static <T> boolean allMatchOneOf(Stream<T> stream, Predicate<T>... predicates) {
    int length = predicates.length;
    BitSet bitSet = new BitSet(length);
    bitSet.set(0, length);
    Iterator<T> it = stream.iterator();
    while (it.hasNext() && !bitSet.isEmpty()) {
        T t = it.next();
        IntStream.range(0, length).filter(i -> !predicates[i].test(t)).forEach(bitSet::clear);
    }
    return !bitSet.isEmpty();
}

样本用法:

// false because not all elements are either even or divisible by 3
System.out.println(allMatchOneOf(Stream.of(2, 3, 12), i -> i % 2 == 0, i -> i % 3 == 0));

// true because all elements are divisible by 3
System.out.println(allMatchOneOf(Stream.of(3, 12, 18), i -> i % 2 == 0, i -> i % 3 == 0));

如果我们想要保持并行支持,我们可以从StreamEx库获得帮助,该库包含filteringfirstpairing收藏家。我们重复使用anyMatching收藏家wrote in this answer

import static one.util.streamex.MoreCollectors.*;

@SafeVarargs
static <T> Collector<T, ?, Boolean> allMatchingOneOf(Predicate<T> first, Predicate<T>... predicates) {
    Collector<T, ?, Boolean> collector = allMatching(first);
    for (Predicate<T> predicate : predicates) {
        collector = pairing(collector, allMatching(predicate), Boolean::logicalOr);
    }
    return collector;
}

static <T> Collector<T, ?, Boolean> allMatching(Predicate<T> pred) {
    return collectingAndThen(anyMatching(pred.negate()), b -> !b);
}

static <T> Collector<T, ?, Boolean> anyMatching(Predicate<T> pred) {
    return collectingAndThen(filtering(pred, first()), Optional::isPresent);
}

新的allMatchingOneOf收集器通过对其执行逻辑OR来组合每个allMatching收集结果的结果。因此,它将告诉流的所有元素是否与给定谓词之一匹配。

样本用法:

// false because not all elements are either even or divisible by 3
System.out.println(Stream.of(2, 3, 12).collect(allMatchingOneOf(i -> i % 2 == 0, i -> i % 3 == 0)));

// true because all elements are divisible by 3
System.out.println(Stream.of(3, 12, 18).collect(allMatchingOneOf(i -> i % 2 == 0, i -> i % 3 == 0)));

答案 1 :(得分:0)

  

我可以执行两次此操作,但之后流将被终止,我需要重复初步操作。

如果您只想在检查条件后处理元素,那么无论如何都必须缓冲流,因为只有遍历了所有元素后才能检查条件。

因此,您的选项是生成流两次或将其放入集合中。