找到流的最小元素,但如果它<= N则提前拯救

Jef*_*oom 7 java java-8 java-stream

我想找到一个大的(数亿个元素)IntStream的最小元素,但我只能使用结果,如果它是> N,所以我想在找到一个元素<= N时拯救我.我期望最小值在大多数情况下<= N.

IntStream.min()不会短路,所以我会被困在处理所有元素.将军IntStream.reduce也不会短路.

IntStream.noneMatch(x -> x <= N)将确保最小元素> N并且如果不是则确实短路,但实际上并没有告诉我最小值.我必须在谓词中维护状态(并添加同步或限于顺序流)以记住实际的最小值.或者,我可以增加N并再次尝试,可能在可能的N范围内进行某种二分搜索,但这听起来既缓慢又复杂.

一旦知道<= N,我怎样才能找到IntStream的最小值,短路?

Stu*_*rks 3

我的第一个想法是使用findAny,但后来我重新阅读了这个问题。:-)

当然,不同之处在于findAny(或findFirst) 一旦找到匹配元素就会短路。如果找到不匹配的元素,您希望短路,然后减少或累积其余的元素。

虽然积累是突变的一种形式,并且被 FP 纯粹主义者所反对,但它有时确实派上用场。Java 8 有一些很好的补充,比如LongAccumulator以低争用的方式累积原始值,使它们适合并行处理。您可以将累积步骤放入peek流操作中。

不幸的是,没有“IntAccumulator”,因此您必须使用long值进行处理。您可以将源转换为 aLongStream或将int值映射为long值。

完成此操作后,使用 处理短路可能是最简单的allMatch。当然,如果allMatch返回 false,则说明发生了短路,累加器实际上并没有最小值。但到目前为止,它应该具有最小值,可能是触发短路的值。

把它放在一起看起来像这样:

IntStream istream = ... 
LongAccumulator acc = new LongAccumulator(Long::min, Long.MAX_VALUE);

if (istream.mapToLong(i -> i).peek(acc::accumulate).allMatch(i -> i > N)) {
    System.out.println("min was " + acc.get());
} else {
    System.out.println("a value was <= " + N);
}
Run Code Online (Sandbox Code Playgroud)