Sequenced Collections
Before Java 21, accessing the first or last element of a collection was inconsistent across types. Sequenced collections (Java 21) fix this with a unified interface.
The Problem
// Before Java 21 — different API for the same concept
// First element:
list.get(0); // List
deque.getFirst(); // Deque
set.iterator().next(); // SortedSet — verbose!
map.firstEntry(); // SortedMap
// Last element:
list.get(list.size() - 1); // List — awkward
deque.getLast(); // Deque
// SortedSet? LinkedHashSet? — no direct method!
map.lastEntry(); // SortedMap
Every collection had its own way — or no way at all.
The Solution: Three New Interfaces
SequencedCollection<E>
├── getFirst()
├── getLast()
├── addFirst(E)
├── addLast(E)
├── removeFirst()
├── removeLast()
└── reversed()
│
┌───────────┴───────────┐
SequencedSet<E> SequencedMap<K,V>
(no duplicates) ├── firstEntry()
├── lastEntry()
├── pollFirstEntry()
├── pollLastEntry()
├── putFirst(K,V)
├── putLast(K,V)
├── reversed()
└── sequencedKeySet()
sequencedValues()
sequencedEntrySet()
Which Classes Implement What
| Class | Implements |
|---|---|
ArrayList |
SequencedCollection |
LinkedList |
SequencedCollection |
ArrayDeque |
SequencedCollection |
LinkedHashSet |
SequencedSet |
TreeSet |
SequencedSet |
LinkedHashMap |
SequencedMap |
TreeMap |
SequencedMap |
SequencedCollection in Action
// Works the same for ArrayList, LinkedList, ArrayDeque, etc.
SequencedCollection<String> names = new ArrayList<>(List.of("Alice", "Bob", "Charlie"));
names.getFirst(); // "Alice"
names.getLast(); // "Charlie"
names.addFirst("Zara"); // ["Zara", "Alice", "Bob", "Charlie"]
names.addLast("Dan"); // ["Zara", "Alice", "Bob", "Charlie", "Dan"]
names.removeFirst(); // removes "Zara"
names.removeLast(); // removes "Dan"
// Reversed view (not a copy!)
SequencedCollection<String> reversed = names.reversed();
reversed.getFirst(); // "Charlie"
reversed.getLast(); // "Alice"
// Iterate in reverse
for (String name : names.reversed()) {
System.out.println(name);
}
SequencedSet
SequencedSet<String> set = new LinkedHashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Cherry");
set.getFirst(); // "Apple"
set.getLast(); // "Cherry"
set.reversed(); // ["Cherry", "Banana", "Apple"]
SequencedMap
SequencedMap<String, Integer> map = new LinkedHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
map.firstEntry(); // a=1
map.lastEntry(); // c=3
map.pollFirstEntry(); // removes and returns a=1
map.pollLastEntry(); // removes and returns c=3
map.putFirst("z", 26); // insert at beginning
map.putLast("m", 13); // insert at end
// Reversed
map.reversed().forEach((k, v) -> System.out.println(k + "=" + v));
// Sequenced views
map.sequencedKeySet(); // SequencedSet<String>
map.sequencedValues(); // SequencedCollection<Integer>
map.sequencedEntrySet(); // SequencedSet<Map.Entry<String, Integer>>
reversed() — A View, Not a Copy
var list = new ArrayList<>(List.of(1, 2, 3));
var rev = list.reversed();
rev.getFirst(); // 3
list.add(4);
rev.getFirst(); // 4 — reflects changes!
rev.addFirst(99); // adds 99 at the END of original list
list; // [1, 2, 3, 4, 99]
reversed()returns a live view — changes propagate both ways.
Before/After Comparison
| Operation | Before Java 21 | Java 21+ |
|---|---|---|
| First of List | list.get(0) |
list.getFirst() |
| Last of List | list.get(list.size()-1) |
list.getLast() |
| First of Set | set.iterator().next() |
set.getFirst() |
| Reverse iterate | ListIterator / copy + reverse |
collection.reversed() |
| First of Map | map.entrySet().iterator().next() |
map.firstEntry() |
Common Mistakes
| Mistake | Problem | Fix |
|---|---|---|
Calling getFirst() on empty collection |
NoSuchElementException |
Check isEmpty() first |
Treating reversed() as a copy |
Modifications reflect back | Be aware it’s a view |
Using addFirst() on unmodifiable collections |
UnsupportedOperationException |
Use on mutable collections only |
Calling addFirst() on TreeSet |
UnsupportedOperationException — order is natural |
Use LinkedHashSet for insertion-order |
Key Takeaways
SequencedCollectionaddsgetFirst(),getLast(),reversed()to all ordered collections.SequencedSetandSequencedMapextend the concept to sets and maps.reversed()returns a live view — not a copy.ArrayList,LinkedHashSet,LinkedHashMap,TreeSet,TreeMapall implement it.- Consistency — one API for first/last access across all collection types.
Module Complete
You’ve completed the Modern Java (8 → 21) module! You now know:
- Java 8 foundations (lambdas, streams, Optional)
- Java 11 additions (var, HttpClient, String methods)
- Java 14–16 innovations (records, text blocks, pattern matching)
- Java 17 LTS (sealed classes, strong encapsulation)
- Java 21 LTS (virtual threads, switch patterns, sequenced collections)
- Deep dives on text blocks, switch expressions, sealed classes, and structured concurrency