Java Sequenced Collections (Java 21+)
Java 21 (JEP 431) introduced three new interfaces (SequencedCollection, SequencedSet, and SequencedMap) to provide a uniform API for accessing the first and last elements of ordered collections. Previously, getting the first or last element of a LinkedHashMap or last element of a List required awkward workarounds.
Contents
The Problem Before Java 21
Before Java 21, there was no consistent way to access the first or last element of an ordered collection. Each collection type had its own awkward workaround: get(size()-1)iterator().next()
// Getting last element of a List — no clean API
List<String> list = List.of("a", "b", "c");
// Before Java 21 — awkward
String last = list.get(list.size() - 1); // IndexOutOfBoundsException if empty
// Getting first/last of a LinkedHashMap — even worse
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 3);
// Getting first entry — very awkward
Map.Entry<String, Integer> first = map.entrySet().iterator().next();
// Getting last entry — genuinely painful
Map.Entry<String, Integer> last = null;
for (Map.Entry<String, Integer> e : map.entrySet()) last = e; // O(n)!
// or: ((LinkedHashMap<String,Integer>) map).sequencedEntrySet().last()
// ... which didn't exist before Java 21
// Getting first/last of a LinkedHashSet
LinkedHashSet<String> set = new LinkedHashSet<>(List.of("x","y","z"));
String firstElem = set.iterator().next(); // only way before Java 21
The New Interfaces
- SequencedCollection<E>: extends
: addsCollection<E> ,getFirst() ,getLast() ,addFirst() ,addLast() ,removeFirst() ,removeLast() .reversed() - SequencedSet<E>: extends
andSequencedCollection<E> :Set<E> returns areversed() .SequencedSet - SequencedMap<K,V>: extends
: addsMap<K,V> ,firstEntry() ,lastEntry() ,pollFirstEntry() ,pollLastEntry() ,putFirst() ,putLast() ,reversed() ,sequencedKeySet() ,sequencedValues() .sequencedEntrySet()
SequencedCollection
SequencedCollectionListDequeLinkedHashSetgetFirst()getLast()addFirst()addLast()removeFirst()removeLast()reversed()
// List implements SequencedCollection
List<String> list = new ArrayList<>(List.of("a", "b", "c", "d"));
// Clean first/last access
String first = list.getFirst(); // "a"
String last = list.getLast(); // "d"
// Add/remove at ends
list.addFirst("Z"); // ["Z", "a", "b", "c", "d"]
list.addLast("X"); // ["Z", "a", "b", "c", "d", "X"]
list.removeFirst(); // removes "Z"
list.removeLast(); // removes "X"
// reversed() — returns a view in reverse order (not a copy)
List<String> rev = list.reversed();
System.out.println(rev); // [d, c, b, a]
rev.forEach(System.out::println); // iterates in reverse
// Deque also implements SequencedCollection
Deque<Integer> deque = new ArrayDeque<>(List.of(1, 2, 3));
System.out.println(deque.getFirst()); // 1
System.out.println(deque.getLast()); // 3
// LinkedHashSet implements SequencedSet
LinkedHashSet<String> set = new LinkedHashSet<>(List.of("x", "y", "z"));
System.out.println(set.getFirst()); // "x"
System.out.println(set.getLast()); // "z"
SequencedSet<String> revSet = set.reversed();
System.out.println(revSet.getFirst()); // "z"
getFirst()getLast()NoSuchElementExceptionget(0)IndexOutOfBoundsExceptionSequencedMap
SequencedMapMapLinkedHashMapTreeMapNavigableMapfirstEntry()lastEntry()pollFirstEntry()pollLastEntry()putFirst()putLast()reversed()sequencedKeySet()sequencedValues()sequencedEntrySet()
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 3);
// First and last entries — clean!
Map.Entry<String, Integer> first = map.firstEntry(); // one=1
Map.Entry<String, Integer> last = map.lastEntry(); // three=3
System.out.println(first.getKey() + "=" + first.getValue()); // one=1
System.out.println(last.getKey() + "=" + last.getValue()); // three=3
// Poll (retrieve and remove)
Map.Entry<String, Integer> polled = map.pollFirstEntry(); // removes "one"
System.out.println(map.size()); // 2
// putFirst/putLast — insert at beginning/end
map.putFirst("zero", 0); // {zero=0, two=2, three=3}
map.putLast("four", 4); // {zero=0, two=2, three=3, four=4}
// reversed() — reversed view
SequencedMap<String, Integer> rev = map.reversed();
System.out.println(rev.firstEntry()); // four=4
// Sequenced views — for iteration in insertion order
SequencedCollection<String> keys = map.sequencedKeySet();
SequencedCollection<Integer> values = map.sequencedValues();
SequencedCollection<Map.Entry<String,Integer>> entries = map.sequencedEntrySet();
Which Collections Implement These
- SequencedCollection:
(ArrayList, LinkedList, Vector, Stack),List (ArrayDeque, LinkedList),Deque ,LinkedHashSet (TreeSet).SortedSet - SequencedSet:
,LinkedHashSet (TreeSet),SortedSet .NavigableSet - SequencedMap:
,LinkedHashMap (TreeMap),SortedMap .NavigableMap - Not included:
,HashSet ,HashMap . These have no defined encounter order.PriorityQueue
// TreeMap (SortedMap → SequencedMap) — natural key order
TreeMap<String, Integer> treeMap = new TreeMap<>();
treeMap.put("banana", 2);
treeMap.put("apple", 1);
treeMap.put("cherry", 3);
System.out.println(treeMap.firstEntry()); // apple=1 (alphabetical)
System.out.println(treeMap.lastEntry()); // cherry=3
// Useful pattern: last-N elements from a sorted map
TreeMap<Long, String> events = new TreeMap<>();
// ... populated with timestamp → event ...
// Get last 3 events:
events.reversed().sequencedEntrySet().stream().limit(3)
.forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));