Java8中 findAny()和 findFirst()的区别

我对 Java8中 StreamAPI 的 Stream#findAny()Stream#findFirst()没有什么困惑。

我所理解的是,两者都将返回流中的第一个匹配元素,例如,当与 filter 一起使用时?

那么,为什么同一个任务有两种方法? 我漏掉了什么吗?

61756 次浏览

What I understood is that both will return the first matched element 从流,例如,当与过滤器一起使用?

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

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

while Stream.findFirst() will return an Optional<T> describing 严格来说 the first element of the stream. The Stream class doesn't have a .findOne() method, so I suppose you meant .findFirst().

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

来自 Stream.findAny()(重点是我的) :

返回一个描述流的 some elementOptional,或者如果流为空则返回一个空的 Optional

这是一个短路终端操作。

此操作的行为是显式的不确定性的; 它可以自由地选择流中的任何元素。这是为了在并行操作中实现最大性能; 代价是同一源上的多个调用可能不会返回相同的结果。(如果需要稳定的结果,则使用 findFirst()。)

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

With the current Oracle-specific implementation, I believe that it will return the first element in a non-parallel pipeline. However, in a parallel pipeline, it won't always, executing for example

System.out.println(IntStream.range(0, 100).parallel().findAny());

当我运行它时,它返回了 OptionalInt[50]。无论如何,你必须依靠它。

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

我写了一些代码片段来展示差异,去看看

在 stream findFirst 和 findAny 中返回 first 元素,并且不执行其余元素,但是在 allelStream 中,不可能说该顺序,而 allelStream 执行集合的其余部分。

参考文献

Time 1:25:00

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

我只想说,当使用 findFirst()findAny()时要小心。

从 Javadoc (here给你)两个方法都从流返回任意元素——除非流有一个 遭遇顺序,在这种情况下,findFirst()返回第一个元素,而 findAny()返回任何元素。

假设我们有由 ISBN 和 BOOK 名称组成的定制 list。 要获得一个场景,请看下面的例子:

public class Solution {
private Integer ISBN;
private String BookName;


public Solution(int i, String string) {
ISBN =i;
BookName = string;
}
//getters and setters
}

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

输出 : Optional[Java in Action]

可能会出现书名相同但 ISBN 编号不同的情况,在这种情况下,排序和查找书籍可以非常类似于 findAny(),并会给出错误的结果。想象一个场景,其中5本书被命名为“ Java Reference”,但是有不同的 ISBN 编号,按名称命名的 findFirst()图书将得到与 findAny()相同的结果。

设想一个场景:

 ISBN    Name Of book
+-----+------------------+
| 100 | Java-8 in Action |
+-----+------------------+
| 101 | Java-8 in Action |
+-----+------------------+
| 102 | Java-8 in Action |
+-----+------------------+
| 103 | Java-8 in Action |
+-----+------------------+
| 104 | Java-8 in Action |
+-----+------------------+

在这里,即使在 < strong > BookByName. 上排序,findFirst ()和 findAny ()也会给出相同的结果

详细文章:

Stream无序时,findFirst()findAny()是相同的,但当 Stream有序时,findAny()会更好。

[1]我已经开发了一个小代码来测试 findAnyandfindFirst of stream。我已经创建了1000万个虚拟数据,并对这些数据进行过滤,然后使用并行流和顺序流对这两个数据应用 findAny 和 findFirst。

通过这个小实验,我发现 findAny 和 findFirst 给出了相同的结果,并且它在顺序流中提供了第一个结果。但是我发现 findAny 并不能保证每次在并行数据流中都产生相同的结果,而 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