Sequenced Collections in Java 21
Java has continuously evolved to meet the changing needs of developers. One area of constant improvement is the collections framework, which provides a set of classes and interfaces to store and manipulate groups of objects. Starting from JDK 21, JEP 431 introduces three Java Collections Framework interfaces for creating sequenced collections, sequenced sets, and sequenced maps. These features simplify interacting with collections that have a defined order, enhancing code readability and reducing boilerplate. This article will explore what Sequenced Collections offer and how they elevate our Java coding experience.
1. Understanding Sequenced Collections
The need to introduce sequenced interfaces stems from a longstanding request for easy methods to retrieve a collection’s first and last elements. Before JDK 21, Java Collections Framework didn’t have a collection type representing a sequence with a specific order.
Java 21 introduces three new interfaces:
- SequencedCollection
- SequencedSet
- SequencedMap
These interfaces provide a uniform API for:
- Accessing first and last elements: No more searching or iterating just to grab the head or tail.
- Adding/removing elements at both ends: Push and pop operations become cleaner and more intuitive.
- Iterating in reverse: Get a reversed view of the collection with a single method call.
2. Getting Started with Sequenced Collections
Sequenced Collections are seamlessly integrated into existing APIs. Most existing collection classes like List and Deque already implement the relevant interfaces. This means we can start reaping the benefits without extensive code changes.
2.1 SequencedCollection
SequencedCollection
represents any collection with a defined order. It provides a standardized way to access and manipulate elements at both the beginning and end of a collection.
The SequencedCollection
interface provides methods for adding, retrieving, and removing elements from both ends along with a method named reversed()
which provides a reverse-ordered view of the original collection.
2.1.1 Example
public class SequencedCollectionExamples { public static void main(String[] args) { SequencedCollection<String> sequencedCollection = new ArrayList<>(); sequencedCollection.add("Send Email"); // List contains [Send Email] sequencedCollection.addFirst("Fix Bug"); // List contains [Fix Bug, Send Email] sequencedCollection.addLast("Write Report"); // List contains [Fix Bug, Send Email, Write Report] System.out.println("" + sequencedCollection); String firstElement = sequencedCollection.getFirst(); String lastElement = sequencedCollection.getLast(); System.out.println("" + firstElement); // Fix Bug System.out.println("" + lastElement); // Write Report SequencedCollection reversed = sequencedCollection.reversed(); System.out.println(reversed); // Prints [Write Report, Send Email, Fix Bug] } }
The output from running the code above is:
2.2 SequencedSet
SequencedSet
extends SequencedCollection
and guarantees unique elements in the order they were added. The SequencedSet
interface is designed for Set
implementations like LinkedHashSet
and SortedSet
.
Key features include:
- Unique elements: No duplicates allowed.
- Defined order: Elements have a specific encounter order, from first to last.
- Reversed view: You can get a reversed view of the set with
reversed()
method. - Methods for accessing and manipulating elements at both ends.
2.2.1 Example
public class SequencedSetExample { public static void main(String[] args) { SequencedSet<String> uniqueNames = new LinkedHashSet<>(); uniqueNames.add("Charles"); uniqueNames.add("Thomas"); uniqueNames.add("Dickson"); String firstElement = uniqueNames.getFirst(); String lastElement = uniqueNames.getLast(); System.out.println("" + firstElement); // Charles System.out.println("" + lastElement); // Dickson uniqueNames.addFirst("Sarah"); System.out.println("" + uniqueNames); // Set contains: [Sarah, Charles, Thomas, Dickson] uniqueNames.addLast("Bill"); System.out.println("" + uniqueNames); // Set contains: [Sarah, Charles, Thomas, Dickson, Bill] System.out.println(uniqueNames.reversed()); //Prints [Bill, Dickson, Thomas, Charles, Sarah] } }
In the code above:
- We use the
getFirst()
method to get the first element. Note that the program will throw aNoSuchElementException
if the collection is empty. - To get the last element, we use the
getLast()
method. - To add a value as the first element, we use the
addFirst()
method. Note that If the value already exists, it will be removed as the first element. - To add a value as the last element, we use the
addLast()
method. uniqueNames.reversed()
method is used to reverse the order of the elements.
2.3 SequencedMap
SequencedMap
is an interface that extends Map
and provides a standardized way to work with maps that have a defined encounter order for their entries.
SequencedMap
‘s are reversible and the reverse order of the map is typically not serializable, even if the original map is.
Key features include:
- Encounter order for entries: Remembers the sequence in which
key-value
pairs were added. - Methods for accessing and manipulating entries at both ends: Offers methods like
putFirst()
andputLast()
for efficient manipulation of the map’s ends. - Reversed view: Provides a reversed view of the map’s entries with
reversed()
method. - Sequenced views for keys, values, and entries: Offers
sequencedKeys()
,sequencedValues()
, andsequencedEntrySet()
methods for accessing views that respect the encounter order.
2.3.1 Example
public class SequencedMapExample { public static void main(String[] args) { SequencedMap<String, Integer> scores = new LinkedHashMap<>(); scores.put("John", 95); scores.put("Omos", 82); scores.put("James", 72); System.out.println("" + scores.firstEntry()); // John=95 (prints the value of the first entry) System.out.println("" + scores.lastEntry()); // James=72 (prints the value of the last entry) System.out.println(scores); // prints {John=95, Omos=82, James=72} SequencedMap.Entry<String, Integer> first = scores.pollFirstEntry(); SequencedMap.Entry<String, Integer> last = scores.pollLastEntry(); System.out.println("Map contains: " + scores); // prints {Omos=82} scores.putFirst("Jerry", 75); scores.putLast("Sarah", 80); System.out.println(scores); // {Jerry=75, Omos=82, Sarah=80} System.out.println(scores.reversed()); // {Sarah=80, Omos=82, Jerry=75} } }
In the code above:
- The first entry of the map is obtained by using the
firstEntry()
method. - To get the last entry of the map, we use the
lastEntry()
method. - The
putFirst()
method is used to put an entry as the first one. - The
putLast()
method is used to put an entry as the last one. - To get and remove the first entry, we use the
pollFirstEntry()
method. - To get and remove the last entry, we use the
pollLastEntry()
method. - The order of the entries is reversed by using the
reversed()
method.
3. Key Points
- Most existing ordered collections implicitly implement
SequencedCollection
, so we can often cast them to use the new methods. SequencedCollection
offers a consistent API for working with the beginning and end of collections, simplifying common tasks and improving code readability.SequencedSet
andSequencedMap
provide additional features for unique elements andkey-value
pairs, respectively, while maintaining order.- Most existing ordered collections like
List
andDeque
already implement the appropriate interfaces.
4. Conclusion
The introduction of Sequenced Collections in Java 21 marks a significant improvement for developers working with ordered collections. By offering a consistent and intuitive API, this feature boosts code readability, reduces boilerplate, and unlocks new possibilities for building efficient and expressive Java applications.
5. Download the Source Code
This was an example of Sequenced Collections in Java 21.
You can download the full source code of this example here: Sequenced Collections in Java 21