6.4 Sequenced Collections in Java 21
Key Takeaways
- Java 21 introduces JEP 431 with three core interfaces: SequencedCollection<E>, SequencedSet<E>, and SequencedMap<K, V> to represent collections with a defined encounter order.
- SequencedCollection provides uniform first and last element access: getFirst(), getLast(), addFirst(), addLast(), removeFirst(), removeLast(), and reversed().
- The reversed() method produces an O(1) live, mutating reverse-ordered view where changes in the view immediately reflect in the backing collection and vice versa.
- LinkedHashSet implements SequencedSet with special relocation semantics: addFirst(e) and addLast(e) relocate an existing element to the requested endpoint.
- Standard hash collections (HashSet and HashMap) do NOT implement Sequenced interfaces because they lack a defined encounter order.
Sequenced Collections in Java 21 (JEP 431)
One of the most impactful features introduced in Java SE 21 and heavily tested on the 1Z0-830 exam is Sequenced Collections (JEP 431). Prior to Java 21, the Java Collections Framework lacked a unified hierarchy to represent collections with a defined encounter order, consistent first/last element access, and uniform reverse-order iteration.
1. The Historical Gap in the Collections Framework
Before Java 21, different collection types had disparate, inconsistent APIs for accessing endpoint elements and iterating in reverse:
| Collection Type | Access First Element | Access Last Element | Reverse Iteration |
|---|---|---|---|
List | list.get(0) | list.get(list.size() - 1) | ListIterator moving backward |
Deque | deque.getFirst() | deque.getLast() | deque.descendingIterator() |
SortedSet | set.first() | set.last() | ((NavigableSet) set).descendingSet() |
LinkedHashSet | set.iterator().next() | Requires full iteration $O(n)$! | No direct API (requires copying) |
This fragmentation created friction: there was no common interface for "a collection that has a first element, a last element, and can be traversed in reverse."
JEP 431 retrofits the Java Collections Framework by introducing three new core interfaces:
SequencedCollection<E>SequencedSet<E>SequencedMap<K, V>
2. The SequencedCollection<E> Interface Architecture
SequencedCollection<E> represents a collection whose elements have a well-defined encounter order (a first element, a second element, and so on up to the last element).
package java.util;
public interface SequencedCollection<E> extends Collection<E> {
// Reverse-ordered view
SequencedCollection<E> reversed();
// Endpoint access methods
void addFirst(E e);
void addLast(E e);
E getFirst();
E getLast();
E removeFirst();
E removeLast();
}
Retrofitted Interface Hierarchy
SequencedCollection is integrated into the existing hierarchy as a parent of List, Deque, and SequencedSet:
Collection<E>
▲
│
SequencedCollection<E>
▲ ▲ ▲
│ │ │
List<E> Deque<E> SequencedSet<E>
▲ ▲
│ │
LinkedHashSet<E> SortedSet<E> / NavigableSet<E>
Method Semantics and Exception Behavior
getFirst()/getLast(): Returns the first or last element. If the collection is empty, throwsNoSuchElementException.removeFirst()/removeLast(): Removes and returns the first or last element. If the collection is empty, throwsNoSuchElementException.addFirst(e)/addLast(e): Inserts element at the designated endpoint. ThrowsUnsupportedOperationExceptionif the collection is unmodifiable or if the collection is sorted (TreeSet).
List<String> list = new ArrayList<>(List.of("Alpha", "Beta", "Gamma"));
System.out.println(list.getFirst()); // "Alpha"
System.out.println(list.getLast()); // "Gamma"
list.addFirst("First");
list.addLast("Last");
System.out.println(list); // [First, Alpha, Beta, Gamma, Last]
String removed = list.removeFirst(); // "First"
[!CAUTION] Empty Collection Exception: Unlike
Deque.peekFirst()/pollFirst()which returnnullon an empty deque,SequencedCollection.getFirst()andremoveFirst()throwNoSuchElementExceptionwhen called on an empty collection!
3. Bidirectional reversed() Views
The reversed() method returns a live, reverse-ordered view of the underlying sequenced collection. It does not copy or clone elements, operating in $O(1)$ time and memory.
Live View Mutability Dynamics
Because reversed() produces a dynamic view rather than a snapshot:
- Mutations made to the
reversed()view are immediately reflected in the original collection. - Mutations made to the original collection are immediately visible in the reversed view.
- Calling
addFirst()on the reversed view appends the element to the end of the original collection! - Calling
removeFirst()on the reversed view removes the last element of the original collection! - Invoking
.reversed().reversed()returns the original collection instance.
List<String> original = new ArrayList<>(List.of("A", "B", "C"));
SequencedCollection<String> reversedView = original.reversed();
System.out.println(reversedView); // [C, B, A]
// Adding to the head of reversed view -> Appends to the tail of original!
reversedView.addFirst("Z");
System.out.println("Original: " + original); // [A, B, C, Z]
System.out.println("Reversed: " + reversedView); // [Z, C, B, A]
// Removing from the head of reversed view -> Removes from tail of original!
reversedView.removeFirst(); // Removes "Z"
System.out.println("Original: " + original); // [A, B, C]
4. SequencedSet<E> and LinkedHashSet Relocation Semantics
SequencedSet<E> extends both Set<E> and SequencedCollection<E>, combining set uniqueness with a defined encounter order:
package java.util;
public interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {
@Override
SequencedSet<E> reversed();
}
Implementations include:
LinkedHashSet<E>: Maintains insertion/access encounter order.TreeSet<E>(viaSortedSet/NavigableSet): Maintains sorted order.
Relocation Semantics in LinkedHashSet (High-Yield Exam Topic!)
In standard Set.add(e) behavior, if an element already exists in the set, the operation is a no-op and returns false without modifying the encounter order.
However, in Java 21, LinkedHashSet provides special relocation semantics for addFirst() and addLast():
addFirst(e): Ifeis already present, it is relocated from its current position to the first position of the encounter order!addLast(e): Ifeis already present, it is relocated to the last position of the encounter order!
SequencedSet<String> set = new LinkedHashSet<>(List.of("A", "B", "C", "D"));
System.out.println("Initial: " + set); // [A, B, C, D]
// Standard add() does NOT change position of existing element
boolean added = set.add("B"); // returns false
System.out.println("After add('B'): " + set); // [A, B, C, D]
// addFirst() RELOCATES existing element "C" to the front!
set.addFirst("C");
System.out.println("After addFirst('C'): " + set); // [C, A, B, D]
// addLast() RELOCATES existing element "A" to the end!
set.addLast("A");
System.out.println("After addLast('A'): " + set); // [C, B, D, A]
5. The SequencedMap<K, V> Interface API
SequencedMap<K, V> represents a map whose key-value mappings have a defined encounter order.
package java.util;
public interface SequencedMap<K, V> extends Map<K, V> {
// Reverse-ordered map view
SequencedMap<K, V> reversed();
// Boundary entry access
Entry<K, V> firstEntry();
Entry<K, V> lastEntry();
Entry<K, V> pollFirstEntry();
Entry<K, V> pollLastEntry();
// Boundary insertions / relocations
V putFirst(K key, V value);
V putLast(K key, V value);
// Sequenced collection views
SequencedSet<K> sequencedKeySet();
SequencedCollection<V> sequencedValues();
SequencedSet<Entry<K, V>> sequencedEntrySet();
}
Implementations of SequencedMap
LinkedHashMap<K, V>: ImplementsSequencedMap.putFirst()andputLast()insert or relocate mappings to the requested endpoint.TreeMap<K, V>(viaNavigableMap<K, V>): ImplementsSequencedMap. However, callingputFirst()orputLast()throwsUnsupportedOperationExceptionbecause entry positions are determined strictly by key comparisons!
SequencedMap<String, Integer> map = new LinkedHashMap<>();
map.putLast("Two", 2);
map.putLast("Three", 3);
map.putFirst("One", 1); // Inserts at head
System.out.println(map); // {One=1, Two=2, Three=3}
System.out.println(map.firstEntry()); // One=1
System.out.println(map.lastEntry()); // Three=3
// Polling retrieves AND removes the boundary entry
Map.Entry<String, Integer> polled = map.pollFirstEntry(); // Removes One=1
System.out.println("Polled: " + polled); // One=1
System.out.println("Remaining: " + map); // {Two=2, Three=3}
6. Why HashSet and HashMap Do NOT Implement Sequenced Interfaces
A fundamental rule for the exam:
HashSetdoes NOT implementSequencedCollectionorSequencedSet.HashMapdoes NOT implementSequencedMap.HashtableandConcurrentHashMapdo NOT implementSequencedMap.
Hash tables distribute keys and elements into hash buckets based on hashCode(). Their iteration order depends on hash distribution, table capacity, and collisions. Because standard hash tables have no defined encounter order, implementing sequenced interfaces on them would violate the interface contract.
7. Unsupported Operations and Immutability
| Collection Instance | getFirst() / getLast() | addFirst() / addLast() | reversed() |
|---|---|---|---|
ArrayList, LinkedList | Supported | Supported | Supported (mutable view) |
ArrayDeque | Supported | Supported | Supported (mutable view) |
LinkedHashSet | Supported | Supported (relocates) | Supported (mutable view) |
TreeSet / TreeMap | Supported | Throws UnsupportedOperationException | Supported (descendingSet view) |
List.of(...) | Supported | Throws UnsupportedOperationException | Supported (unmodifiable view) |
HashSet / HashMap | Not Available (Compile error) | Not Available | Not Available |
Unmodifiable Sequenced Collection Factory Wrappers
Java 21 added static wrapper methods in java.util.Collections:
SequencedCollection<T> unmodColl = Collections.unmodifiableSequencedCollection(seqColl);
SequencedSet<T> unmodSet = Collections.unmodifiableSequencedSet(seqSet);
SequencedMap<K, V> unmodMap = Collections.unmodifiableSequencedMap(seqMap);
What is the expected outcome of running the following Java 21 code snippet? List<String> emptyList = new ArrayList<>(); String first = emptyList.getFirst();
Consider the following Java 21 program: List<String> list = new ArrayList<>(List.of("A", "B", "C")); var rev = list.reversed(); rev.addFirst("Z"); System.out.println(list); What is printed to standard output?
Examine the following code using a LinkedHashSet in Java 21: SequencedSet<String> set = new LinkedHashSet<>(List.of("A", "B", "C")); set.addFirst("B"); System.out.println(set); What is the resulting encounter order printed by this code?
Which of the following statements regarding the SequencedMap interface in Java 21 is FALSE?