Java 8中findAny()和findFirst()之间的区别

时间:2016-02-12 09:34:43

标签: java java-8 java-stream

我对java 8中Stream#findAny() API的Stream#findFirst()Stream感到困惑。

我的理解是两者都会从流中返回第一个匹配的元素,例如,当与过滤器一起使用时?

那么,为什么两种方法可以完成相同的任务呢?我错过了什么吗?

8 个答案:

答案 0 :(得分:65)

  

我的理解是两者都将返回第一个匹配的元素   例如,当与过滤器一起使用时,来自流?

那不是真的。根据javadoc,Stream#findAny()

  

返回描述流的某些元素的Optional<T>,或者为空   如果流为空,则Optional<T>。   此操作的行为明确是不确定的; 可以自由选择流中的任何元素。这是为了在并行操作中实现最大性能;

Stream.findFirst()将返回Optional<T>,描述严格流的第一个元素。 Stream课程没有.findOne()方法,因此我认为您的意思是.findFirst()

答案 1 :(得分:34)

不,两者都不会返回Stream的第一个元素。

来自Stream.findAny()(强调我的):

  

返回描述流的某个元素Optional,如果流为空则返回空Optional

     

这是一种短路终端操作。

     

此操作的行为明确是不确定的; 可以自由选择流中的任何元素。这是为了在并行操作中实现最大性能;成本是同一源上的多次调用可能不会返回相同的结果。 (如果需要稳定的结果,请改用findFirst()。)

所以更简单地说,它可能会也可能不会选择Stream的第一个元素。

使用当前的Oracle特定实现,我相信它将返回非并行管道中的第一个元素。但是,在并行管道中,它不会总是(例如System.out.println(IntStream.range(0, 100).parallel().findAny());执行,当我运行它时返回OptionalInt[50])。无论如何,你必须依赖它。

答案 2 :(得分:4)

findFirst返回流的第一个元素,但是findAny可以自由选择流中的任何元素。

        List<String> lst1 = Arrays.asList("Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");
        List<String> lst2 = Arrays.asList("Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");

        Optional<String> findFirst = lst1.parallelStream().filter(s -> s.startsWith("D")).findFirst();
        Optional<String> fidnAny = lst2.parallelStream().filter(s -> s.startsWith("J")).findAny();

        System.out.println(findFirst.get()); //Always print David
        System.out.println(fidnAny.get()); //Print Jack/Jill/Julia :behavior of this operation is explicitly nondeterministic

答案 3 :(得分:1)

在并行模式下,findAny不能保证顺序,但是findFirst可以保证顺序。

我写了一些代码片段来显示差异,visit it

答案 4 :(得分:1)

我只是说,在使用时要当心class UserMiddleware(object): def process_request(self, request): if not request.get_full_path().endswith('/settings/force/') \ and request.user and not request.user.is_anonymous() \ and request.institute.force_student_data and not request.user.is_data_collected(request.institute): return redirect(reverse('settings:force_update')) return None findFirst()

从Javadoc(herehere)开始,这两种方法都从流中返回任意元素-除非流具有遇到顺序,在这种情况下,{ {1}}返回第一个元素,而findAny()将返回任何元素。

假设我们具有由ISBN和BOOK名称组成的自定义findFirst()。 对于这种情况,请看以下示例:

findAny()

list

输出public class Solution { private Integer ISBN; private String BookName; public Solution(int i, String string) { ISBN =i; BookName = string; } //getters and setters }

在某些情况下,书名相同但ISBN号不同,在这种情况下,对书的分类和查找可能与public static void main(String[] args) { List<Solution> Library = Arrays.asList(new Solution(12,"Java in Action"),new Solution(13,"Java 8"),new Solution(15,"Java 8 Features"),new Solution(16,"Java in Action")); System.out.println(Library.stream() .map(p->p.getBookName()) .sorted(Comparator.reverseOrder()) .findFirst()); } 非常相似,并且会给出错误的结果。考虑这样一种情况,其中有5本书被命名为“ Java参考”,但是具有不同的ISBN号,按名称命名的Optional[Java in Action]本书将与findAny()相同。

考虑以下情况:

findFirst()

在这里,即使按 BookByName排序,findFirst()和findAny()也会给出相同的结果。

Detailed Article:

答案 5 :(得分:1)

[1]我开发了一个小代码来测试流的 findAny 和 findFirst。我已经创建了 1000 万个虚拟数据并过滤了这些数据,并通过在两者上使用并行流和顺序流来应用 findAny 和 findFirst。

从小实验中,我发现 findAny 和 findFirst 给出了相同的结果,并且它提供了顺序流中的第一个结果。但我发现 findAny 不能保证每次在 parallelStream 中产生相同的结果,而 findFirst 保证它总是产生相同的结果,无论流是并行的还是顺序的。

public class TestFilter {
static class Employee {
    String name;
    double salary;
    long id;

    public Employee(String name, long id, double salary) {
        this.name = name;
        this.id = id;
        this.salary = salary;
    }

    String genNextName() {
        return "Emp-" + (this.id + 1);
    }

    double genNextSalary() {
        return new Random().nextDouble() * 1000000;
    }

    long genNextId() {
        return this.id + 1;
    }

    @Override
    public String toString() {
        return this.id + " " + this.name + " " + this.salary + "\n";
    }
}

public static void main(String[] args) {
    List<Employee> employees = createDummyEmployee(10000000l);
    List<Employee> emps = null;
    long time = 0;
    for (int i = 0; i < 50; i++) {
        
        Optional<Employee> e1 = employees.stream()
                .filter(e -> e.name.endsWith("999"))
                .filter(e -> e.salary > 10000)
                .filter(e -> e.id % 2 == 1)
                .findAny();
        
        Optional<Employee> e2 = employees.stream()
                .filter(e -> e.name.endsWith("999"))
                .filter(e -> e.salary > 10000)
                .filter(e -> e.id % 2 == 1)
                .findFirst();
        
        Optional<Employee> pe1 = employees.parallelStream()
                .filter(e -> e.name.endsWith("999"))
                .filter(e -> e.salary > 10000).filter(e -> e.id % 2 == 1)
                .findAny();
        
        Optional<Employee> pe2 = employees.parallelStream()
                .filter(e -> e.name.endsWith("999"))
                .filter(e -> e.salary > 10000)
                .filter(e -> e.id % 2 == 1)
                .findFirst();

        System.out.print("FindAny without parallel : " + (e1.isPresent() ? e1.get().id +"": "null"));
        System.out.print(" | FindFirst without parallel : " + (e2.isPresent() ? e2.get().id +"": "null"));
        System.out.print(" | FindAny by Parallel : " + (pe1.isPresent() ? pe1.get().id +"": "null"));
        System.out.print(" | FindFirst by Parallel : " + (pe2.isPresent() ? pe2.get().id +"": "null"));
        System.out.println();
    }
}

public static List<Employee> createDummyEmployee(long n) {
    Employee e1 = new Employee("Emp-1", 1l, 1.0);
    return Stream.iterate(e1, (Employee e) -> new Employee(e.genNextName(), e.genNextId(), e.genNextSalary()))
            .limit(n).collect(Collectors.toList());
}

}

【实验结果】 [1]:https://i.stack.imgur.com/HOZjA.png

答案 6 :(得分:0)

在流findFirst和findAny中返回第一个元素,不执行其余元素,但是在parallelStream中,说顺序是不可行的,而parallelStream执行集合的其余部分。

Reference

时间1:25:00

答案 7 :(得分:-1)

Stream无序时,findFirst()findAny()相同。但是订购Stream时,findAny()会更好。