Java 通过谓词查找第一个元素

Java 通过谓词查找第一个元素,java,java-8,java-stream,Java,Java 8,Java Stream,我刚刚开始使用Java8Lambdas,我正在尝试用函数式语言实现一些我习惯的东西 例如,大多数函数式语言都有某种对序列进行操作的find函数,或者返回谓词为true的第一个元素的列表。在Java 8中实现这一点的唯一方法是: lst.stream() .filter(x -> x > 5) .findFirst() 然而,这对我来说似乎效率低下,因为过滤器将扫描整个列表,至少在我看来是这样(这可能是错误的)。有更好的办法吗 否,过滤器不会扫描整个流。它是一个中间操

我刚刚开始使用Java8Lambdas,我正在尝试用函数式语言实现一些我习惯的东西

例如,大多数函数式语言都有某种对序列进行操作的find函数,或者返回谓词为
true
的第一个元素的列表。在Java 8中实现这一点的唯一方法是:

lst.stream()
    .filter(x -> x > 5)
    .findFirst()

然而,这对我来说似乎效率低下,因为过滤器将扫描整个列表,至少在我看来是这样(这可能是错误的)。有更好的办法吗

否,过滤器不会扫描整个流。它是一个中间操作,返回一个惰性流(实际上所有中间操作都返回一个惰性流)。要说服您,您只需执行以下测试:

List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);
int a = list.stream()
            .peek(num -> System.out.println("will filter " + num))
            .filter(x -> x > 5)
            .findFirst()
            .get();
System.out.println(a);
您可以看到,实际上只处理流的前两个元素

所以你可以用你的方法,这是非常好的

然而,这对我来说似乎效率低下,因为过滤器将扫描整个列表

不,它不会-一旦找到满足谓词的第一个元素,它就会“中断”。你可以阅读更多关于懒惰的文章,特别是(我的重点):

许多流操作(如筛选、映射或重复删除)都可以延迟实现,从而提供了优化的机会。例如,“查找包含三个连续元音的第一个字符串”不需要检查所有输入字符串。流操作分为中间(产生流)操作和终端(产生价值或副作用)操作中间操作总是懒惰的。

我必须从对象列表中只筛选出一个对象。所以我使用了这个,希望它能有所帮助。

除了用户的答案之外,如果您使用的是数组列表,您不确定要搜索的元素是否存在,请使用这个

Integer a = list.stream()
                .peek(num -> System.out.println("will filter " + num))
                .filter(x -> x > 5)
                .findFirst()
                .orElse(null);

然后,您只需检查a是否为
null

改进的一行回答:如果您要查找布尔返回值,我们可以通过添加isPresent来做得更好:

return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().isPresent();

导入org.junit.Test;
导入java.util.array;
导入java.util.List;
导入java.util.Optional;
//对于相同的操作,流速度大约慢30倍。。。
公共类流性能测试{
int迭代次数=100;
List=Arrays.asList(1,10,3,7,5);
//55毫秒
@试验
公共空流(){
对于(int i=0;ix>5)
.findFirst();
System.out.println(result.orElse(null));
}
}
//2毫秒
@试验
公共void循环(){
对于(int i=0;i5){
结果=步行;
打破
}
}
系统输出打印项次(结果);
}
}
}

已由@AjaxLeung回答,但在评论中很难找到。
仅供检查

lst.stream()
    .filter(x -> x > 5)
    .findFirst()
    .isPresent()
简化为

lst.stream()
    .anyMatch(x -> x > 5)

这不是低效的,Java8流实现是惰性的,所以过滤器只应用于终端操作。同样的问题:酷。这就是我希望它能做到的。否则的话,这将是一个重大的设计失败。如果您真的想检查列表是否包含这样的元素(而不是从可能的几个元素中挑出第一个),那么.findAny()理论上在并行设置中会更有效,当然也会更清楚地传达这一意图。与简单的forEach循环相比,这将在堆上创建大量对象和几十个动态方法调用。虽然这可能并不总是影响性能测试的底线,但在热点中,避免琐碎地使用流和类似的重量级结构是有区别的。这个答案对我来说更具信息性,并且解释了为什么,而不仅仅是如何。我从来没有新的中间操作总是懒惰;Java流继续让我感到惊讶在这里,因为我知道我将哪些值馈送到流管道,因此会有结果。实际上,您不应该使用
get()
,但是
orElse()
/
orElseGet()
/
orelsetrow()
(用于更有意义的错误而不是NSEE),因为您可能不知道应用于流管道的操作是否会产生元素。
.findFirst().orElse(null)例如,不要使用orElse null。这应该是一种反模式。所有这些都包含在可选选项中,那么为什么您要冒NPE的风险呢?我认为处理选择题是更好的方法。在使用可选项之前,请先用isPresent()测试它。@BeJay我不明白。我应该用什么来代替
orElse
?@JohnHenckel我认为BeJay的意思是你应该把它作为
可选的
类型,这就是
.findFirst
返回的。Optional的一个用途是帮助开发人员避免处理
null
s。e、 g.不检查
myObject!=null
,您可以选中myOptional.isPresent()
,或使用可选界面的其他部分。这清楚了吗?你应该修正你的例子。您不能将null分配给纯整数。我已经编辑了您的帖子。在整数列表中搜索时,0(零)可能是有效结果。将变量类型替换为整数,将默认值替换为null。更好:由于我们正在查找布尔返回值,因此可以通过添加null检查来更好地执行此操作:return dataSource.getParkingLots().stream().filter(parkingLot->Objects.equals(parkingLot.getId(),id)).findFirst().orElse(null)!=无效@shreedharbhat您不需要执行
.orElse(null)!=空
。相反,请使用可选API的
.isPresent
,即
.findFirst().isPresent()
@shr

import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

// Stream is ~30 times slower for same operation...
public class StreamPerfTest {

    int iterations = 100;
    List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);


    // 55 ms
    @Test
    public void stream() {

        for (int i = 0; i < iterations; i++) {
            Optional<Integer> result = list.stream()
                    .filter(x -> x > 5)
                    .findFirst();

            System.out.println(result.orElse(null));
        }
    }

    // 2 ms
    @Test
    public void loop() {

        for (int i = 0; i < iterations; i++) {
            Integer result = null;
            for (Integer walk : list) {
                if (walk > 5) {
                    result = walk;
                    break;
                }
            }
            System.out.println(result);
        }
    }
}

lst.stream()
    .filter(x -> x > 5)
    .findFirst()
    .isPresent()
lst.stream()
    .anyMatch(x -> x > 5)