Sitemap
Javarevisited

A humble place to learn Java and Programming better.

Sequenced Collections: A Deep Dive into JDK 21’s addition to Collections Framework

--

Sequenced collections introduced in Java 21 provide a unified way to work with ordered collections and make your code cleaner and more expressive while working with ordered collections. Some Collections in Java Collections framework has a defined encounter order which means they maintain the order of elements. For example, List, Deque and LinkedHashset are ordered collections, but they all didn't have common semantics or a common super interface for this ordering till Java 21. Sequenced collections in Java 21 fill this gap by introducing three new interfaces: SequencedCollection, SequencedSet, and SequencedMap. These interfaces provide a unified way to work with ordered collections, making it easier to write generic code that can handle any ordered collection. Also this introduces new methods to work with first and last elements, and a reversed() method to get a reversed view of the collection.

Changes added in Sequenced Collections

Now let’s explore the new interfaces and methods introduced in JDK 21’s Sequenced Collections API.

1. SequencedCollection

This interface extends Collection and adds methods for working with elements at both ends of an ordered collection:

public interface SequencedCollection<E> extends Collection<E> {
// Adds element at the end
default boolean addLast(E e);

// Adds element at the beginning
boolean addFirst(E e);

// Gets the first element
E getFirst();

// Gets the last element
E getLast();

// Removes and returns the first element
E removeFirst();

// Removes and returns the last element
E removeLast();

// Returns a reversed view of this collection
SequencedCollection<E> reversed();
}

2. SequencedSet

This interface extends both Set and SequencedCollection, representing an ordered collection that requires unique elements:

public interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {
// Override to return a SequencedSet
@Override
SequencedSet<E> reversed();
}

3. SequencedMap

This interface extends Map and adds methods for working with the first and last entries:

public interface SequencedMap<K, V> extends Map<K, V> {
// Map entry operations
Map.Entry<K, V> firstEntry();
Map.Entry<K, V> lastEntry();
Map.Entry<K, V> pollFirstEntry();
Map.Entry<K, V> pollLastEntry();

// Add entries at either end
default V putFirst(K k, V v);
default V putLast(K k, V v);

// Returns a reversed view
SequencedMap<K, V> reversed();

// Returns sequenced views of the map
SequencedSet<K> sequencedKeySet();
Collection<V> sequencedValues();
SequencedSet<Entry<K, V>> sequencedEntrySet();
}

Practical Examples

Now, let’s explore some practical examples of how these new interfaces can make your code cleaner and more expressive.

Example 1: Working with the First and Last Elements

Before JDK 21, getting the first or last element of a List required using indexed access:

// Before JDK 21
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie", "David"));
String first = names.get(0); // Get first element
String last = names.get(names.size() - 1); // Get last element

// Remove the first element
names.remove(0);
// Remove the last element
names.remove(names.size() - 1);

With JDK 21’s Sequenced Collections:

// With JDK 21
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie", "David"));
String first = names.getFirst(); // Cleaner syntax
String last = names.getLast(); // No need to calculate index

names.removeFirst(); // Simpler
names.removeLast(); // More intuitive

All these 4 methods getFirst(), getLast(), removeFirst(), and removeLast() are now available for all ordered collections, including List, Deque, LinkedHashSet, NavigableSet etc. since they all implement SequencedCollection.

Example 2: Iterating in Reverse Order

Before JDK 21, iterating a collection in reverse order varied by collection type:

// For List - using ListIterator
List<String> list = Arrays.asList("A", "B", "C", "D");
ListIterator<String> iterator = list.listIterator(list.size());
while (iterator.hasPrevious()) {
System.out.println(iterator.previous());
}

// For Deque - using descendingIterator
Deque<String> deque = new ArrayDeque<>(Arrays.asList("A", "B", "C", "D"));
Iterator<String> descIterator = deque.descendingIterator();
while (descIterator.hasNext()) {
System.out.println(descIterator.next());
}
// For LinkedHashSet - not straightforward, usually required copying to another collection
LinkedHashSet<String> set = new LinkedHashSet<>(Arrays.asList("A", "B", "C", "D"));
List<String> reversedList = new ArrayList<>(set);
Collections.reverse(reversedList);
for (String item : reversedList) {
System.out.println(item);
}

With JDK 21, you can use the uniform reversed() method:

// Works for any SequencedCollection
List<String> list = Arrays.asList("A", "B", "C", "D");
for (String s : list.reversed()) {
System.out.println(s); // Prints D, C, B, A
}

Deque<String> deque = new ArrayDeque<>(Arrays.asList("A", "B", "C", "D"));
for (String s : deque.reversed()) {
System.out.println(s); // Prints D, C, B, A
}
LinkedHashSet<String> set = new LinkedHashSet<>(Arrays.asList("A", "B", "C", "D"));
for (String s : set.reversed()) {
System.out.println(s); // Prints D, C, B, A
}

Example 3: Working with LinkedHashSet

Before JDK 21, reordering elements in a LinkedHashSet was difficult:

// Before JDK 21
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("A");
set.add("B");
set.add("C");
// Output: [A, B, C]

// To move B to the end - requires removing and re-adding
set.remove("B");
set.add("B");
// Output: [A, C, B]
// Getting first/last elements was complicated
Iterator<String> iterator = set.iterator();
String first = iterator.hasNext() ? iterator.next() : null;
// Getting last element was even worse - had to iterate through everything
String last = null;
for (String s : set) {
last = s;
}

With JDK 21:

// With JDK 21
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("A");
set.add("B");
set.add("C");
// Output: [A, B, C]

// Move B to the end
set.addLast("B"); // Automatically removes B from its original position and adds it at the end
// Output: [A, C, B]
// Getting first/last elements is straightforward
String first = set.getFirst(); // Returns "A"
String last = set.getLast(); // Returns "B"

Example 4: Working with Sequenced Maps

Before JDK 21, working with the first or last entries of an ordered map was cumbersome:

// Before JDK 21
LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");

// Getting first entry
Map.Entry<Integer, String> firstEntry = null;
for (Map.Entry<Integer, String> entry : map.entrySet()) {
firstEntry = entry;
break;
}
// Getting last entry
Map.Entry<Integer, String> lastEntry = null;
for (Map.Entry<Integer, String> entry : map.entrySet()) {
lastEntry = entry;
}

With JDK 21:

// With JDK 21
LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");

// Getting first and last entries is simple
Map.Entry<Integer, String> firstEntry = map.firstEntry(); // Entry(1, "One")
Map.Entry<Integer, String> lastEntry = map.lastEntry(); // Entry(3, "Three")
// Putting an entry at the beginning or end
map.putFirst(0, "Zero"); // Now: {0=Zero, 1=One, 2=Two, 3=Three}
map.putLast(4, "Four"); // Now: {0=Zero, 1=One, 2=Two, 3=Three, 4=Four}
// If key already exists, it gets moved to the requested position
map.putFirst(3, "Three"); // Moves key 3 to the beginning
// Now: {3=Three, 0=Zero, 1=One, 2=Two, 4=Four}

Example 5: Working with Reversed Views

The reversed() method returns a view of the original collection with elements in reverse order. Any changes to the view affect the underlying collection, and vice versa:

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
List<String> reversed = list.reversed();

System.out.println(list); // [A, B, C]
System.out.println(reversed); // [C, B, A]
// Modifications to the reversed view affect the original list
reversed.set(0, "Z"); // Modifies the last element of original list
System.out.println(list); // [A, B, Z]
System.out.println(reversed); // [Z, B, A]
// Adding to the original affects the reversed view
list.add("D");
System.out.println(list); // [A, B, Z, D]
System.out.println(reversed); // [D, Z, B, A]
// Removing from the reversed view affects the original
reversed.remove(0); // Removes the last element from the original
System.out.println(list); // [A, B, Z]
System.out.println(reversed); // [Z, B, A]

Example 6: Creating a Reversed Stream

Before JDK 21, streaming a list in reverse order required somewhat complex code:

// Before JDK 21
List<String> list = Arrays.asList("A", "B", "C");

// Creating a reverse-ordered stream (verbose)
Stream<String> reversedStream = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(
new Iterator<String>() {
private final ListIterator<String> listIter = list.listIterator(list.size());

public boolean hasNext() {
return listIter.hasPrevious();
}

public String next() {
return listIter.previous();
}
},
Spliterator.ORDERED
),
false
);

With JDK 21:

// With JDK 21
List<String> list = Arrays.asList("A", "B", "C");

// Creating a reverse-ordered stream (clean)
Stream<String> reversedStream = list.reversed().stream();

When to Use Sequenced Collections

The Sequenced Collections API is particularly useful when:

  1. Writing generic algorithms that need to work with any ordered collection, regardless of whether it’s a List, Deque, LinkedHashSet, etc.
  2. Creating APIs that need to accept or return ordered collections without forcing a specific implementation.
  3. Working with the endpoints of collections, such as accessing the first or last element.
  4. Iterating in reverse without having to remember different approaches for different collection types.

Conclusion

The Sequenced Collections API provides a consistent and powerful way to work with ordered collections in Java.

To stay updated with the latest updates in Java and Spring, follow us on , , and Medium.

References

Javarevisited
Javarevisited

Published in Javarevisited

A humble place to learn Java and Programming better.

Code Wiz
Code Wiz

Written by Code Wiz

Software engineering and programming tutorials and blogs. Website - Youtube -

No responses yet