Contents

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) for List, iterating to the end for LinkedHashMap, or calling iterator().next() for LinkedHashSet. None of these were readable, and some were O(n).

// 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

SequencedCollection is the base interface extended by List, Deque, and LinkedHashSet. It adds getFirst(), getLast(), addFirst(), addLast(), removeFirst(), removeLast(), and reversed() — a reversed view of the same underlying collection (not a copy).

// 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() and getLast() throw NoSuchElementException if the collection is empty — unlike the old get(0) which throws IndexOutOfBoundsException.

SequencedMap extends Map and is implemented by LinkedHashMap, TreeMap, and NavigableMap. It adds firstEntry(), lastEntry(), pollFirstEntry(), pollLastEntry(), putFirst(), putLast(), and reversed(), as well as sequencedKeySet(), sequencedValues(), and sequencedEntrySet() for ordered iteration.

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();
// 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()));