按谓词查找第一个元素

sik*_*iki 478 java java-8 java-stream

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

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

lst.stream()
    .filter(x -> x > 5)
    .findFirst()
Run Code Online (Sandbox Code Playgroud)

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

Ale*_* C. 677

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

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);
Run Code Online (Sandbox Code Playgroud)

哪个输出:

will filter 1
will filter 10
10
Run Code Online (Sandbox Code Playgroud)

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

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

  • 作为一个注释,我在这里使用`get();`因为我知道我向流管道提供了哪些值,因此会有结果.在实践中,你不应该使用`get();`,而是`orElse()`/`orElseGet()`/`orElseThrow()`(对于更有意义的错误而不是NSEE),因为你可能不知道是否应用于流管道的操作将产生一个元素. (36认同)
  • `.findFirst().orElse(null);`例如 (30认同)
  • 不要使用orElse null.这应该是反模式.它都包含在Optional中,为什么要冒NPE的风险呢?我认为处理Optional是更好的方法.在使用之前,只需使用isPresent()测试Optional即可. (18认同)
  • @JohnHenckel我认为BeJay的意思是你应该将其保留为“Optional”类型,这就是“.findFirst”返回的内容。Optional 的用途之一是帮助开发人员避免处理“null”seg,而不是检查“myObject != null”,您可以检查“myOptional.isPresent()”,或使用Optional接口的其他部分。这样是不是更清楚了? (5认同)

小智 102

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

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

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

  • 这个答案对我来说更有用,并解释了原因,而不仅仅是如何.我从来没有新的中间操作总是懒惰; Java流继续让我感到惊讶. (5认同)

Cod*_*dow 30

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

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

  • @shreedharbhat你不需要做`.orElse(null) != null`。相反,请使用可选 API 的“.isPresent”,即“.findFirst().isPresent()”。 (2认同)
  • @shreedharbhat 首先,OP 并不是在寻找布尔返回值。其次,如果是的话,编写 `.stream().map(ParkingLot::getId).anyMatch(Predicate.isEqual(id))` 会更干净 (2认同)

GKi*_*lin 16

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

lst.stream()
    .filter(x -> x > 5)
    .findFirst()
    .isPresent()
Run Code Online (Sandbox Code Playgroud)

被简化为

lst.stream()
    .anyMatch(x -> x > 5)
Run Code Online (Sandbox Code Playgroud)


Ife*_*yan 13

除了Alexis C的回答之外,如果您正在使用数组列表,您不确定您要搜索的元素是否存在,请使用此列表.

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

然后,你可以简单地检查是否一个null.

  • 你应该修正你的例子。您不能将 null 分配给普通 int。/sf/ask/157810481/ (2认同)