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

时间:2015-03-02 01:06:07

标签: java java-8 java-stream

我想找到一个大的(数亿个元素)IntStream的最小元素,但我只能使用结果,如果它是&gt; N,所以我想在找到一个元素&lt; = N时立即拯救。我希望最小值在大部分时间都是<= N.

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

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

如何知道IntStream的最小值,一旦知道&lt; = N?

就会短路

2 个答案:

答案 0 :(得分:3)

你可以使用anyMatch作弊。注意,这是未经测试的,并且是粗略的。请注意,虽然它有副作用,但内部lambda显然不是有关流的状态。

final AtomicInteger min=new AtomicInteger(Integer.MAX_VALUE);
final int minLimit=?;
boolean isValid=!stream.anyMatch(i->{
    if(i<=minLimit){
        return true;
    }
    min.getAndAccumulate(i, Math::min);
    return false;
});

这可以通过利用anyMatch的短路来实现。

实际上,如果不将状态存储在lambda之外,则可能无法做到这一点。这是因为为了做到这一点,我们必须是有状态的,到目前为止存储最小值,短路并且具有用户定义的行为。 docs表示IntStream上没有这样的运算符。(只有限制是有状态和短路的)。这似乎是一个关于阻塞/并行的问题。

- Update-- 找到关于并行性评论的官方source

“除了被识别为明确不确定的操作(例如findAny())之外,流是顺序执行还是并行执行不应该更改计算结果。

大多数流操作接受描述用户指定行为的参数,这些参数通常是lambda表达式。为了保持正确的行为,这些行为参数必须是非干扰的,并且在大多数情况下必须是无状态的。这些参数总是函数接口的实例,例如Function,通常是lambda表达式或方法引用。“

看起来问题是用户提供的行为,而不是短路。

答案 1 :(得分:3)

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

当然,差异在于findAny(或findFirst)一旦找到匹配元素就会短路。如果找到不匹配的元素,则需要短路,然后减少或累积其余元素。

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

不幸的是,没有&#34; IntAccumulator&#34;所以你必须使用long值进行处理。您可以将来源转换为LongStream或将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);
}