Java 21 Sequenced Collections Explained
Introduction
Many Java collections have a defined order - a
First, last and reversed on lists and sets
OUTPUT of the above code
Explanation of the code
OUTPUT of the above code
Writing methods against the new interfaces
The new interfaces are also useful as parameter types, for methods which need an ordered collection but do not care which kind -
OUTPUT of the above code
List, a Deque, a LinkedHashSet, a TreeSet, a LinkedHashMap. Yet before Java 21 they had no common way to get the first or last element, or to process the elements in reverse. Getting the last element of a List meant list.get(list.size() - 1); of a LinkedHashSet, iterating through all of it. Java 21Click to read tutorial: Overview of New Features in Java 21 (JEP 431) added sequenced collections: three new interfaces, SequencedCollection, SequencedSet and SequencedMap, which give every ordered collection the same methods for its first and last elements and a reversed view. This tutorial explains them with examples.
The new interfaces
SequencedCollection<E>- a collection with a defined encounter order. Methods:getFirst(),getLast(),addFirst(e),addLast(e),removeFirst(),removeLast()andreversed().ListandDequenow extend it.SequencedSet<E>- a sequenced collection with no duplicates;reversed()returns aSequencedSet.LinkedHashSetimplements it, andSortedSetextends it.SequencedMap<K,V>- a map with a defined order of entries. Methods:firstEntry(),lastEntry(),pollFirstEntry(),pollLastEntry(),putFirst(k, v),putLast(k, v),reversed(), and sequenced viewssequencedKeySet(),sequencedValues()andsequencedEntrySet().LinkedHashMapimplements it, andSortedMapextends it.
getFirst, getLast and reversed on different collections
//SequencedCollectionsDemo.java
import java.util.*;
public class SequencedCollectionsDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>(List.of("Tom", "Harry", "Sam"));
System.out.println("List first/last : " + list.getFirst() + " / " + list.getLast()); // was get(size()-1)
list.addFirst("Ann"); // was add(0, "Ann")
System.out.println("After addFirst : " + list);
System.out.println("Reversed view : " + list.reversed());
LinkedHashSet<String> set = new LinkedHashSet<>(List.of("red", "green", "blue"));
System.out.println("Set first/last : " + set.getFirst() + " / " + set.getLast()); // was impossible without iterating
set.addFirst("blue"); // already present: moved to the front
System.out.println("After addFirst : " + set);
TreeSet<Integer> sorted = new TreeSet<>(List.of(30, 10, 20));
System.out.println("TreeSet reversed: " + sorted.reversed());
Deque<Integer> deque = new ArrayDeque<>(List.of(1, 2, 3));
System.out.println("Deque last : " + deque.getLast() + ", reversed " + deque.reversed());
}
}
List first/last : Tom / Sam After addFirst : [Ann, Tom, Harry, Sam] Reversed view : [Sam, Harry, Tom, Ann] Set first/last : red / blue After addFirst : [blue, red, green] TreeSet reversed: [30, 20, 10] Deque last : 3, reversed [3, 2, 1]
getFirst()andgetLast()work the same way on aList, aLinkedHashSet, aTreeSetand aDeque. On an empty collection they throwNoSuchElementException.- On a
LinkedHashSet,addFirst()of an element which is already present moves it to the front - so aLinkedHashSetcan now be used as an ordered set whose order you control. reversed()returns a view, not a copy: it costs nothing to create, and changes to the original show through it - and vice versa for modifiable collections.- On a
TreeSet, whose order is determined by its comparator,addFirst()andaddLast()throwUnsupportedOperationException, since the set decides the position itself. Unmodifiable collections, such as those fromList.of(), throw it for all the modifying methods.
SequencedMap methods on a LinkedHashMap
//SequencedMapDemo.java
import java.util.LinkedHashMap;
import java.util.SequencedMap;
public class SequencedMapDemo {
public static void main(String[] args) {
SequencedMap<String, Integer> scores = new LinkedHashMap<>();
scores.put("Tom", 78);
scores.put("Harry", 91);
scores.put("Sam", 64);
System.out.println("First entry : " + scores.firstEntry());
System.out.println("Last entry : " + scores.lastEntry());
scores.putFirst("Ann", 88); // insert (or move) at the front
System.out.println("After putFirst: " + scores);
System.out.println("Reversed keys : " + scores.reversed().sequencedKeySet());
var oldest = scores.pollFirstEntry(); // remove and return the first entry
System.out.println("Polled " + oldest + ", left " + scores);
}
}
First entry : Tom=78
Last entry : Sam=64
After putFirst: {Ann=88, Tom=78, Harry=91, Sam=64}
Reversed keys : [Sam, Harry, Tom, Ann]
Polled Ann=88, left {Tom=78, Harry=91, Sam=64}
pollFirstEntry() and putFirst() make a LinkedHashMap convenient as a simple ordered cache or queue of keyed entries - operations which previously needed workarounds.
A method accepting any sequenced collection
//SequencedParameterDemo.java
import java.util.*;
public class SequencedParameterDemo {
static <E> String ends(SequencedCollection<E> items) {
return items.isEmpty() ? "(empty)" : items.getFirst() + " ... " + items.getLast();
}
public static void main(String[] args) {
System.out.println(ends(List.of(1, 2, 3)));
System.out.println(ends(new ArrayDeque<>(List.of("a", "b"))));
System.out.println(ends(new TreeSet<>(Set.of(5, 1, 9))));
System.out.println(ends(Collections.unmodifiableSequencedCollection(new LinkedHashSet<>(List.of("x")))));
}
}
1 ... 3 a ... b 1 ... 9 x ... x
Collections also got unmodifiableSequencedCollection(), unmodifiableSequencedSet() and unmodifiableSequencedMap(). A HashSet or HashMap, which has no defined order, is deliberately not sequenced, so passing one to ends() is a compile error.
Summary
In this tutorial we saw how Java 21's sequenced collections give every ordered collection a common set of methods. We used getFirst(), getLast(), addFirst() and the reversed() view on lists, sets and deques, and firstEntry(), putFirst() and pollFirstEntry() on a LinkedHashMap. Lastly, we wrote a method which accepts any SequencedCollection. Next, read about generational ZGCClick to read tutorial: Java 21 Generational ZGC Explained.Continue Reading Java 18 to 21 Tutorials
Overview of New Features in Java 21Click to read tutorial: Overview of New Features in Java 21 Java 21 Virtual Threads Tutorial with ExamplesClick to read tutorial: Java 21 Virtual Threads Tutorial with Examples Java 21 Record Patterns with ExamplesClick to read tutorial: Java 21 Record Patterns with Examples Java 21 Pattern Matching for switch with ExamplesClick to read tutorial: Java 21 Pattern Matching for switch with Examples Java 21 Generational ZGC ExplainedClick to read tutorial: Java 21 Generational ZGC Explained Java 21 API Additions: StringBuilder.repeat, Math.clampClick to read tutorial: Java 21 API Additions: StringBuilder.repeat, Math.clamp Overview of New Features in Java 18Click to read tutorial: Overview of New Features in Java 18 Overview of New Features in Java 19Click to read tutorial: Overview of New Features in Java 19 Overview of New Features in Java 20Click to read tutorial: Overview of New Features in Java 20
Overview of New Features in Java 21Click to read tutorial: Overview of New Features in Java 21 Java 21 Virtual Threads Tutorial with ExamplesClick to read tutorial: Java 21 Virtual Threads Tutorial with Examples Java 21 Record Patterns with ExamplesClick to read tutorial: Java 21 Record Patterns with Examples Java 21 Pattern Matching for switch with ExamplesClick to read tutorial: Java 21 Pattern Matching for switch with Examples Java 21 Generational ZGC ExplainedClick to read tutorial: Java 21 Generational ZGC Explained Java 21 API Additions: StringBuilder.repeat, Math.clampClick to read tutorial: Java 21 API Additions: StringBuilder.repeat, Math.clamp Overview of New Features in Java 18Click to read tutorial: Overview of New Features in Java 18 Overview of New Features in Java 19Click to read tutorial: Overview of New Features in Java 19 Overview of New Features in Java 20Click to read tutorial: Overview of New Features in Java 20