findFirst()
Get the first element of a stream as an Optional, turn a missing result into a 404, and know when findAny or min is the better choice.
Optional<T> findFirst()- Added in Java Java 8
Return value
Optional<T>An Optional containing the first element in encounter order, or an empty Optional if the stream is empty. If the stream has no encounter order, any element may be returned.
Errors
NullPointerException if the element selected is null.
findFirst() is a short-circuiting terminal operation that returns the first element of the stream wrapped in an Optional. Together with filter it is the stream version of a search loop with a break: it stops as soon as one element gets through.
Optional<User> user = users.stream()
.filter(u -> u.email().equalsIgnoreCase(email))
.findFirst();Handling the Optional
The result is empty when nothing matched, and the type forces you to decide what that means:
orElseThrow(() -> new NotFoundException(...))for a resource that must exist, which an API layer maps to 404.orElse(defaultValue)for a cheap default,orElseGet(() -> ...)when computing the default costs something.map(...)to pull a field out before choosing a default.
Calling get() without checking throws NoSuchElementException, which surfaces as an unhelpful 500. Prefer orElseThrow with a specific exception.
Null elements
Optional cannot hold null, so if the first element is null, findFirst() throws NullPointerException rather than returning an empty Optional. That happens after a map that can produce null, or on a list built with Arrays.asList. Add filter(Objects::nonNull) before it when nulls are possible.
findFirst versus findAny
On an ordered sequential stream both return the first element in practice, but only findFirst promises it. On a parallel stream findAny can return whichever element a thread finds first, which is cheaper. Use findFirst when the order means something (the newest log entry after sorting, the first rule that matches) and findAny when any match is fine. On a source with no encounter order, such as a HashSet, findFirst gives no meaningful "first" either.
Simpler alternatives
- The first element of a
Listwith no condition:list.getFirst()(Java 21), which throws on an empty list, orlist.stream().findFirst()if you want an Optional. - The smallest or largest by a key:
minormaxwith a comparator, rather thansorted().findFirst(), which sorts everything first. - A lookup by ID repeated many times: build a
Maponce instead of scanning the list on every call.
Example
When to use it
- Find a user by email in a small in-memory list of seeded accounts and return 404 if absent.
- Pick the first matching routing or pricing rule from an ordered list of rules.
- Read the first non-blank value from a list of fallback configuration sources.
Frequently asked questions
What is the difference between findFirst and findAny?
findFirst returns the first element in encounter order. findAny may return any element, which lets parallel streams stop at whichever match a thread finds first. On sequential ordered streams they usually return the same element, but only findFirst guarantees it.
Why does findFirst throw NullPointerException?
The first element of the stream is null, and an Optional cannot contain null. Add .filter(Objects::nonNull) before findFirst, or fix the step that produces nulls.
How do I get the first element that matches a condition, or throw if there is none?
Use stream.filter(condition).findFirst().orElseThrow(() -> new YourException(...)). The no-argument orElseThrow() throws NoSuchElementException if you do not need a custom exception.
More examples
Turning a missing result into a 404
orElseThrow with your own exception gives a clear message that an exception handler can map to a 404 response, instead of a NoSuchElementException from get().
class NotFoundException extends RuntimeException {
NotFoundException(String message) {
super(message);
}
}
record Product(String sku, String name) {}
List<Product> catalogue = List.of(new Product("P1", "Desk"), new Product("P2", "Lamp"));
Product bySku(String sku) {
return catalogue.stream()
.filter(p -> p.sku().equals(sku))
.findFirst()
.orElseThrow(() -> new NotFoundException("product " + sku + " not found"));
}
void main() {
IO.println(bySku("P2").name());
try {
bySku("P9");
} catch (NotFoundException e) {
IO.println("404: " + e.getMessage());
}
}
// Output:
// Lamp
// 404: product P9 not foundA null first element throws
An Optional cannot represent 'present but null', so findFirst refuses with NullPointerException. Filtering nulls first returns the first real value.
void main() {
List<String> values = Arrays.asList(null, "b");
try {
values.stream().findFirst();
} catch (NullPointerException e) {
IO.println("findFirst threw NullPointerException");
}
IO.println(values.stream().filter(Objects::nonNull).findFirst().orElseThrow());
}
// Output:
// findFirst threw NullPointerException
// bfindFirst after sorting versus min
Both pick the highest-priority job, but sorted().findFirst() sorts the whole list while min makes one pass. findAny only promises some element, which is enough for an existence check.
record Job(String name, int priority) {}
void main() {
List<Job> queue = List.of(new Job("email", 3), new Job("billing", 1), new Job("report", 2));
IO.println(queue.stream()
.sorted(Comparator.comparingInt(Job::priority))
.findFirst()
.orElseThrow()
.name());
IO.println(queue.stream()
.min(Comparator.comparingInt(Job::priority))
.orElseThrow()
.name());
IO.println(queue.stream().findAny().isPresent());
}
// Output:
// billing
// billing
// true
Discussion