按谓词查找第一个元素

问题

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

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

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

然而这对我来说似乎效率低下,因为过滤器会扫描整个列表,至少根据我的理解(这可能是错误的)。有没有更好的办法?


#1 热门回答(515 赞)

不,过滤器不会扫描整个流。这是一个中间操作,它返回一个惰性流(实际上所有中间操作都返回一个惰性流)。为了说服你,你可以简单地做以下测试:

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);

哪个输出:

will filter 1
will filter 10
10

你会看到实际只处理了流的两个第一个元素。

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


#2 热门回答(82 赞)

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

不,它不会 - 一旦找到满足谓词的第一个元素,它就会"中断"。你可以在stream package javadoc中阅读更多关于懒惰的内容,特别是(强调我的):

可以懒惰地实现许多流操作,例如过滤,映射或重复删除,从而暴露优化机会。例如,"查找具有三个连续元音的第一个字符串"不需要检查所有输入字符串。流操作分为中间(流生成)操作和终端(生成价值或副作用)操作。中间操作总是很懒惰。


#3 热门回答(21 赞)

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

我不得不从对象列表中过滤出一个对象。所以我用这个,希望它有所帮助。