按谓词查找第一个元素

时间:2014-05-16 13:28:20

标签: java java-8 java-stream

我刚开始玩Java 8 lambdas,我试图实现我在功能语言中习惯的一些东西。

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

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

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

8 个答案:

答案 0 :(得分:626)

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

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

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

所以你可以选择完全没问题的方法。

答案 1 :(得分:96)

  

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

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

  

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

答案 2 :(得分:28)

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

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

答案 3 :(得分:11)

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

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

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

答案 4 :(得分:1)

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

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

简化为

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

答案 5 :(得分:0)

如果您正在寻找布尔返回值,我们可以通过添加空检查来做得更好:

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

答案 6 :(得分:0)


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

答案 7 :(得分:-17)

除非你的列表非常庞大(数千个元素),否则在这里使用流只是昂贵的,甚至使代码更难理解。

注意:java不是一种函数式语言(并且jvm并不特别适合有效地实现函数式语言)。

更简单,更有效率(在所有Iterable&#39;)

for (MyType walk : lst)
    if (walk > 5) { do_whatever; break; }

或者如果你想跳过迭代器:

for (int x=0; x<list.size(); x++)
    if (list.get(x) > 5 { do_whatever; break; }

实际上,我真的很想知道为什么有些人会建议使用这种复杂而昂贵的流媒体机器,即使对于获取阵列的第一个元素等微不足道的事情也是如此。 (是的:Java8中仍支持数组)。