PCSalt
YouTube GitHub
Back to Java
Java · 1 min read

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

InterfaceImplementations
SequencedCollectionArrayList, LinkedList, ArrayDeque, LinkedHashSet, TreeSet
SequencedSetLinkedHashSet, TreeSet
SequencedMapLinkedHashMap, 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

MethodWhat 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.