Java 21 — Sequenced Collections — Finally, a First/Last API
Java 21's Sequenced Collections add getFirst(), getLast(), and reversed() to List, Set, and Map — the API Java has been missing for 25 years.
For 25 years, getting the first and last element of a Java collection required different code depending on the collection type:
// List — by index
list.get(0); // first
list.get(list.size() - 1); // last
// Deque — dedicated methods
deque.getFirst(); // first
deque.getLast(); // last
// SortedSet — different method names
sortedSet.first(); // first
sortedSet.last(); // last
// LinkedHashSet — no direct way!
linkedHashSet.iterator().next(); // first — no clean way to get last
Java 21 fixes this with Sequenced Collections — new interfaces that add getFirst(), getLast(), addFirst(), addLast(), and reversed() to all ordered collections.
The new interfaces
SequencedCollection<E> extends Collection<E>
├── getFirst(), getLast()
├── addFirst(E), addLast(E)
├── removeFirst(), removeLast()
└── reversed()
SequencedSet<E> extends SequencedCollection<E>, Set<E>
SequencedMap<K,V> extends Map<K,V>
├── firstEntry(), lastEntry()
├── putFirst(K, V), putLast(K, V)
├── pollFirstEntry(), pollLastEntry()
├── reversed()
└── sequencedKeySet(), sequencedValues(), sequencedEntrySet()
Basic usage
First and last elements
List<String> list = List.of("a", "b", "c");
String first = list.getFirst(); // "a"
String last = list.getLast(); // "c"
// Works for any SequencedCollection
LinkedHashSet<String> set = new LinkedHashSet<>(List.of("x", "y", "z"));
String firstInSet = set.getFirst(); // "x"
String lastInSet = set.getLast(); // "z"
// Works for SortedSet
TreeSet<Integer> sorted = new TreeSet<>(List.of(3, 1, 2));
int smallest = sorted.getFirst(); // 1
int largest = sorted.getLast(); // 3
Reversed view
List<String> list = List.of("a", "b", "c");
SequencedCollection<String> reversed = list.reversed();
System.out.println(reversed); // [c, b, a]
// Iterate in reverse
for (String s : list.reversed()) {
System.out.println(s); // c, b, a
}
// Stream in reverse
list.reversed().stream().forEach(System.out::println);
reversed() returns a view — it doesn’t create a new collection. Changes to the original are reflected in the reversed view.
Adding to beginning/end
LinkedList<String> list = new LinkedList<>(List.of("b", "c"));
list.addFirst("a"); // [a, b, c]
list.addLast("d"); // [a, b, c, d]
String removed = list.removeFirst(); // "a", list is now [b, c, d]
SequencedMap
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put("alice", 30);
map.put("bob", 25);
map.put("charlie", 35);
Map.Entry<String, Integer> first = map.firstEntry();
// alice=30
Map.Entry<String, Integer> last = map.lastEntry();
// charlie=35
// Iterate in reverse insertion order
for (var entry : map.reversed().entrySet()) {
System.out.println(entry);
}
// charlie=35, bob=25, alice=30
// Sequenced views
SequencedSet<String> keys = map.sequencedKeySet();
String firstKey = keys.getFirst(); // "alice"
String lastKey = keys.getLast(); // "charlie"
putFirst / putLast
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put("b", 2);
map.put("c", 3);
map.putFirst("a", 1); // {a=1, b=2, c=3}
map.putLast("d", 4); // {a=1, b=2, c=3, d=4}
putFirst and putLast control insertion order in LinkedHashMap.
Which collections implement what
| Interface | Implementations |
|---|---|
SequencedCollection | ArrayList, LinkedList, ArrayDeque, LinkedHashSet, TreeSet |
SequencedSet | LinkedHashSet, TreeSet |
SequencedMap | LinkedHashMap, TreeMap |
Unmodifiable collections
List.of(), Set.of(), Map.of() and Collections.unmodifiable*() support getFirst(), getLast(), and reversed() but throw UnsupportedOperationException for addFirst(), addLast(), etc.
Practical examples
Getting boundaries
// Price range from a sorted set
TreeSet<Double> prices = new TreeSet<>(List.of(9.99, 24.99, 14.99, 39.99));
double cheapest = prices.getFirst(); // 9.99
double priciest = prices.getLast(); // 39.99
System.out.println("Range: $%.2f - $%.2f".formatted(cheapest, priciest));
Recent items
// Last N items from insertion-ordered set
LinkedHashSet<String> recentSearches = new LinkedHashSet<>();
recentSearches.add("kotlin coroutines");
recentSearches.add("spring boot 4");
recentSearches.add("jetpack compose");
// Get the most recent search
String mostRecent = recentSearches.getLast(); // "jetpack compose"
// Display in reverse order (most recent first)
recentSearches.reversed().forEach(System.out::println);
Processing queues
// Process first, peek at last
ArrayDeque<Task> queue = new ArrayDeque<>();
queue.addLast(new Task("task-1"));
queue.addLast(new Task("task-2"));
queue.addLast(new Task("task-3"));
Task next = queue.getFirst(); // peek at next
Task newest = queue.getLast(); // peek at newest
Task processed = queue.removeFirst(); // process next
Reverse iteration without creating a new list
// Before Java 21
List<String> logs = getRecentLogs();
for (int i = logs.size() - 1; i >= 0; i--) {
System.out.println(logs.get(i));
}
// Java 21
for (String log : logs.reversed()) {
System.out.println(log);
}
No index math, no off-by-one errors.
Edge cases
Empty collections
List<String> empty = List.of();
empty.getFirst(); // throws NoSuchElementException
empty.getLast(); // throws NoSuchElementException
Check isEmpty() first, or use a pattern:
Optional<String> first = list.isEmpty()
? Optional.empty()
: Optional.of(list.getFirst());
Single element
List<String> single = List.of("only");
single.getFirst(); // "only"
single.getLast(); // "only" (same element)
HashSet (not sequenced)
HashSet<String> set = new HashSet<>(List.of("a", "b", "c"));
// set.getFirst(); // COMPILE ERROR — HashSet is not SequencedCollection
HashSet has no defined order, so it doesn’t implement SequencedCollection. Use LinkedHashSet if you need ordering.
Summary
| Method | What it does |
|---|---|
getFirst() | Returns the first element |
getLast() | Returns the last element |
addFirst(e) | Adds element at the beginning |
addLast(e) | Adds element at the end |
removeFirst() | Removes and returns the first element |
removeLast() | Removes and returns the last element |
reversed() | Returns a reversed view |
Sequenced Collections unify what was previously inconsistent across List, Deque, SortedSet, and LinkedHashSet. One API, works everywhere there’s an ordering. A small feature, but one that makes everyday Java code cleaner.