【问题标题】:Java 8 Stream anyMatch() goes through the whole streamJava 8 Stream anyMatch() 遍历整个流
【发布时间】:2018-01-27 09:59:28
【问题描述】:

给定三个这样的函数:

private Optional<Integer> abc() {
    return Optional.of(6);
}


private Optional<Integer> def() {
    return Optional.of(3);
}


private Optional<Integer> ghi() {
    return Optional.of(9);
}

如果我想检查三个函数之一是否返回大于 5 的值(当然包裹在 Optional 中),以传统的命令式风格,我会这样做:

if( abc().get() > 5 || def().get() > 5 || ghi().get() > 5) {
  ......// Do something
 }  // I am not doing get() without checking ifPresent() just for simplicity sake

这只会进入函数 abc() 并跳过 def()ghi(),因为第一个表达式返回 true。这是一个很好的优化。 现在,如果我使用 Streams 以函数式风格编写相同的代码,

if( Stream.of(abc(), def(), ghi()).anyMatch(integer -> integer.get() > 5)) {
   .........
}

我认为同样会发生,即只会调用 abc()。但它调用了所有三个函数。有anyMatch()的时候检查其他两个函数不是多余吗?

noneMatch()的情况相同;流通过整个流。我只是想知道:即使在第一个元素处满足条件,遍历整个流(特别是如果流有很多值)真的不是一件坏事吗?

【问题讨论】:

标签: java-8 java-stream


【解决方案1】:

这是因为Stream#ofhappens beforeStream#anyMatch,所以所有的方法都会被调用,因为它们发生在Stream#of之前。

您可以使用Supplier&lt;Optional&lt;Integer&gt;&gt; 使Stream#anyMatch 在实际方法调用之前发生,例如:

// Note: it just create Suppliers and actual method is called on demand 
Stream<Supplier<Optional<Integer>>> values=Stream.of(this::abc,this::def,this::ghi);

if(values.anyMatch(integer -> integer.get().get() > 5)) {
    .........
}

正如@FedericoPeraltaSchaffner 已经提到的,Optional 可能为空,您可以使用Optional#orElse(0) 而不是Optional#get,或使用Opitional#filter(it -&gt; it &gt; 5).isPresent()

编辑

为了说明Stream的短路终端操作,您应该使用lambdas/方法引用表达式,因为方法调用发生在Stream#of之前,例如:

Supplier<Optional<Integer>> failsOnMismatched = () -> { 
   throw new IllegalStateException(); 
};

// the instantiation of method reference happen before `Stream#of`,
// but the linked method is called on demand.
//                 v 
if(Stream.of(this::abc, failsOnMismatched).anyMatch(it -> it.get().orElse(0) > 5)){
  //reached
}

//it is failed since the value of def() <= 5 ---v
if(Stream.of(this::def, failsOnMismatched).anyMatch(it -> it.get().orElse(0) > 5)){
  //unreachable
}

【讨论】:

    【解决方案2】:

    如果您使用的是 Java 9 或更高版本,则可以链接选项,应用 Optional.filterOptional.orOptional.ifPresent

    abc().filter(n -> n > 5)
        .or(() -> def().filter(n -> n > 5))
        .or(() -> ghi().filter(n -> n > 5))
        .ifPresent(n -> {
            // do domething
        });
    

    请注意,此解决方案是完整的,即您不需要检查是否存在任何值,因为 Optional.filterOptional.orOptional.ifPresent 已经完成了这项工作。

    【讨论】:

      【解决方案3】:

      Stream#anyMatch 可能不会针对谓词中的所有特定元素进行评估。但是,同时of 也需要参数,这意味着它们将首先被评估。

      返回此流的任何元素是否与提供的匹配 谓词。 如果不是,可能不会评估所有元素的谓词 确定结果所必需的。如果流是空的,那么 返回 false 并且不计算谓词。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-01-06
        • 2017-09-04
        • 1970-01-01
        • 1970-01-01
        • 2017-10-26
        • 2019-11-30
        • 1970-01-01
        相关资源
        最近更新 更多