findFirst vs findAny
Both return an Optional match. Only one of them promises you the first element in order.
Open this lesson in the learning hubKey points
findFirstreturns the earliest match in encounter order, sequential or parallel. That promise costs coordination.findAnyreturns whichever match some worker reached first. On a sequential stream that is almost always the first one too.- On a parallel stream
findAnyis the cheaper call, because no worker has to wait for the ones ahead of it. - Unordered sources such as
HashSethave no encounter order, so the two behave the same there. - Both return
Optional, so an empty result is a value you handle, never a null and never an exception.
Example
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
List<Integer> nums = IntStream.rangeClosed(1, 40).boxed().toList();
System.out.println("findFirst seq : " + nums.stream().filter(n -> n % 5 == 0).findFirst().orElseThrow());
System.out.println("findAny seq : " + nums.stream().filter(n -> n % 5 == 0).findAny().orElseThrow());
// In parallel findFirst still promises the first element in encounter order.
System.out.println("findFirst par : " + nums.parallelStream().filter(n -> n % 5 == 0).findFirst().orElseThrow());
// findAny is allowed to return any match, so run it many times and see what turns up.
Set<Integer> seen = new TreeSet<>();
for (int i = 0; i < 300; i++) {
seen.add(nums.parallelStream().filter(n -> n % 5 == 0).findAny().orElseThrow());
}
System.out.println("findAny par : saw " + seen + " over 300 runs");
System.out.println("no match : " + nums.stream().filter(n -> n > 99).findFirst().isPresent());
}
}
Ask for findFirst only when the order genuinely matters.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Streams course, and every lesson in it is listed on the Streams contents page.