getFirst() and getLast()
Reads the first or last element directly (Java 21), and shows how they fail on empty lists compared with get(0).
default E getFirst()
default E getLast()- Added in Java Java 21
Return value
EThe first or last element of the list.
Errors
NoSuchElementException if the list is empty.
Java 21 added sequenced collections (JEP 431). List now extends SequencedCollection, which gives every list getFirst(), getLast(), addFirst, addLast, removeFirst, removeLast and reversed(). Before that, reading the last element meant list.get(list.size() - 1), which is easy to get wrong and hard to scan.
Behaviour
getFirst() is equivalent to get(0) and getLast() to get(size() - 1), with one difference: on an empty list they throw NoSuchElementException rather than IndexOutOfBoundsException. Neither returns null for an empty list, so you still check isEmpty() first, or wrap the call when absence is a normal case.
They work on every list, including unmodifiable ones from List.of and Stream.toList(), because they only read. removeFirst() and removeLast() need a modifiable list.
Where they fit
- The newest entry of a list ordered by time, such as the latest status of an order.
- The highest version after sorting a list of releases.
- The top row of a result the query already ordered, such as the highest-scoring search hit. If a query must return exactly one row, check
size() == 1instead of silently taking the first.
If an empty list is a normal outcome, return an Optional from your own method: list.isEmpty() ? Optional.empty() : Optional.ofNullable(list.getFirst()). list.stream().findFirst() also works but throws NullPointerException if the first element is null.
Compatibility notes
LinkedList and Deque types already had getFirst and getLast; Java 21 lifted them into the common interface. A custom List implementation, in your code or a library, that already declares getFirst, getLast or reversed with an incompatible return type no longer compiles against Java 21, which is worth checking when you upgrade a large codebase.
removeFirst() on an ArrayList shifts every remaining element. For a work queue that takes from the front repeatedly, use an ArrayDeque.
Example
When to use it
- Reading the most recent status change from an order's ordered history.
- Picking the newest API version or migration after sorting by number.
- Taking the top-ranked hit from a search result that is already sorted by relevance.
Frequently asked questions
Which Java version added List.getFirst and getLast?
Java 21, as part of sequenced collections (JEP 431). On older versions use list.get(0) and list.get(list.size() - 1).
What does getFirst return on an empty list?
It does not return anything; it throws NoSuchElementException. Check isEmpty() first or wrap the call in a helper that returns Optional.
More examples
Latest release after sorting
Once the list is sorted, the ends carry the meaning. getFirst and getLast read them without index arithmetic.
record Release(String version, int build) {}
void main() {
List<Release> releases = new ArrayList<>(List.of(
new Release("2.1.0", 210),
new Release("2.3.0", 230),
new Release("2.2.1", 221)));
releases.sort(Comparator.comparingInt(Release::build));
IO.println("oldest: " + releases.getFirst().version());
IO.println("latest: " + releases.getLast().version());
}
// Output:
// oldest: 2.1.0
// latest: 2.3.0Optional when empty is normal
A lookup that may find nothing returns Optional, so the caller decides between a default and a 404 instead of catching an exception.
<T> Optional<T> first(List<T> list) {
return list.isEmpty() ? Optional.empty() : Optional.ofNullable(list.getFirst());
}
void main() {
List<String> matches = List.of("[email protected]");
List<String> noMatches = List.of();
IO.println(first(matches).orElse("not found"));
IO.println(first(noMatches).orElse("not found"));
}
// Output:
// [email protected]
// not foundDraining a list from the front
removeFirst returns and removes the head in one call. For long queues an ArrayDeque avoids shifting the array on every removal.
void main() {
List<String> jobs = new ArrayList<>(List.of("email", "resize", "report"));
while (!jobs.isEmpty()) {
IO.println("running " + jobs.removeFirst());
}
IO.println("left: " + jobs.size());
}
// Output:
// running email
// running resize
// running report
// left: 0
Discussion