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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23696317/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 00:46:04  来源:igfitidea点击:

Find first element by predicate

javajava-8java-stream

提问by siki

I've just started playing with Java 8 lambdas and I'm trying to implement some of the things that I'm used to in functional languages.

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

For example, most functional languages have some kind of find function that operates on sequences, or lists that returns the first element, for which the predicate is true. The only way I can see to achieve this in Java 8 is:

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

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

However this seems inefficient to me, as the filter will scan the whole list, at least to my understanding (which could be wrong). Is there a better way?

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

采纳答案by Alexis C.

No, filter does not scan the whole stream. It's an intermediate operation, which returns a lazy stream (actually all intermediate operations return a lazy stream). To convince you, you can simply do the following test:

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

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

Which outputs:

哪些输出:

will filter 1
will filter 10
10

You see that only the two first elements of the stream are actually processed.

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

So you can go with your approach which is perfectly fine.

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

回答by wha'eve'

However this seems inefficient to me, as the filter will scan the whole list

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

No it won't - it will "break" as soon as the first element satisfying the predicate is found. You can read more about laziness in the stream package javadoc, in particular (emphasis mine):

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

Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, "find the first String with three consecutive vowels" need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.

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

回答by CodeShadow

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

I had to filter out only one object from a list of objects. So i used this, hope it helps.

我只需要从对象列表中过滤掉一个对象。所以我用了这个,希望它有帮助。

回答by Ifesinachi Bryan

In addition to Alexis C's answer, If you are working with an array list, in which you are not sure whether the element you are searching for exists, use this.

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

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

Then you could simply check whether ais null.

然后你可以简单地检查a是否是null

回答by shreedhar bhat

Improved One-Liner answer:If you are looking for a boolean return value, we can do it better by adding isPresent:

改进的 One-Liner 答案:如果您正在寻找布尔返回值,我们可以通过添加 isPresent 来做得更好:

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

回答by aillusions


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