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

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

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

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


当前回答


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

其他回答

除了Alexis C的回答,如果你正在使用一个数组列表,你不确定你正在搜索的元素是否存在,使用这个。

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

然后你可以简单地检查a是否为空。

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

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

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

@AjaxLeung已经回复了,但在评论中很难找到。 仅供支票使用

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

化简为

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

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

改进的一行回答:如果你正在寻找一个布尔返回值,我们可以通过添加isPresent来做得更好:

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