VOOZH about

URL: https://www.javacodegeeks.com/2025/11/sequenced-collections-api-standardizing-ordered-access.html

⇱ Sequenced Collections API: Standardizing Ordered Access - Java Code Geeks


For decades, Java’s Collections Framework had a glaring inconsistency. Want the first element of a List? Call list.get(0). First element of a TreeSet? Use set.first(). First entry of a LinkedHashMap? You’d better iterate or call entrySet().iterator().next(). This chaos ended with JEP 431, which introduced Sequenced Collections in Java 21—a simple idea that should have existed from day one.

The Problem Nobody Talked About

Java’s collection interfaces evolved organically over 25+ years, and it shows. Each collection type developed its own vocabulary for accessing elements at specific positions. Lists had numerical indexing. Deques had getFirst() and getLast(). Sorted sets had first() and last(). But there was no common abstraction for “collections that have a defined encounter order.”

This wasn’t just an aesthetic problem. It made writing generic algorithms painful. Want to write a method that works with any ordered collection? Good luck. You’d end up with instanceof checks and special cases for each collection type.

// Before Java 21 - the instanceof mess
public static <T> T getFirstElement(Collection<T> collection) {
 if (collection instanceof List) {
 return ((List<T>) collection).get(0);
 } else if (collection instanceof Deque) {
 return ((Deque<T>) collection).getFirst();
 } else if (collection instanceof SortedSet) {
 return ((SortedSet<T>) collection).first();
 } else if (collection instanceof LinkedHashSet) {
 return collection.iterator().next();
 }
 throw new IllegalArgumentException("Unsupported collection type");
}

Every Java developer has written code like this. It’s verbose, error-prone, and fundamentally wrong—we’re checking runtime types instead of relying on interfaces.

Enter Sequenced Collections

The Sequenced Collections API introduces three new interfaces that sit between Collection and the concrete implementations:

  • SequencedCollection – A collection with a defined encounter order
  • SequencedSet – A set with a defined encounter order
  • SequencedMap – A map with a defined encounter order for entries

These interfaces provide uniform methods for accessing and manipulating elements at both ends:

interface SequencedCollection<E> extends Collection<E> {
 SequencedCollection<E> reversed();
 void addFirst(E e);
 void addLast(E e);
 E getFirst();
 E getLast();
 E removeFirst();
 E removeLast();
}

Now your generic algorithm becomes trivial:

// Java 21+ - works with any sequenced collection
public static <T> T getFirstElement(SequencedCollection<T> collection) {
 return collection.getFirst();
}

No instanceof checks. No special cases. Just polymorphism working as intended.

Reversed Views: Efficiency Meets Elegance

The reversed() method is deceptively powerful. It doesn’t create a new collection or copy elements—it returns a live view of the original collection in reverse order. Modifications to the view affect the original, and vice versa.

List<String> list = new ArrayList<>(List.of("A", "B", "C", "D"));
SequencedCollection<String> reversed = list.reversed();

System.out.println(reversed); // [D, C, B, A]

reversed.addFirst("E"); // Adds to the end of original list
System.out.println(list); // [A, B, C, D, E]
System.out.println(reversed); // [E, D, C, B, A]

This is crucial for performance. Reversing a large list now costs O(1) instead of O(n). You’re not copying millions of elements—you’re just changing the perspective on the same data.

The reversed view maintains all the properties of the original collection. If the original is a List, the reversed view supports random access with the same performance characteristics. If it’s a Set, the reversed view maintains set semantics.

Map Symmetry: Finally

Maps were particularly painful before sequenced collections. LinkedHashMap maintains insertion order, but accessing the first or last entry required awkward iteration:

// Old way - verbose and inefficient
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put("first", 1);
map.put("second", 2);

Map.Entry<String, Integer> firstEntry = 
 map.entrySet().iterator().next();
 
// Getting the last entry? Even worse
Map.Entry<String, Integer> lastEntry = null;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
 lastEntry = entry;
}

SequencedMap makes this elegant:

LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put("first", 1);
map.put("second", 2);

Map.Entry<String, Integer> firstEntry = map.firstEntry();
Map.Entry<String, Integer> lastEntry = map.lastEntry();

// Or just the keys/values
String firstKey = map.sequencedKeySet().getFirst();
Integer lastValue = map.sequencedValues().getLast();

The sequencedKeySet() and sequencedValues() methods return SequencedSet and SequencedCollection views respectively, giving you ordered access to map components.

Deque Integration: Smoothing the Rough Edges

Deque already had methods like getFirst() and addLast(), so why change anything? Consistency. The Deque interface now extends SequencedCollection, and its existing methods serve as the implementation of the sequenced contract.

But there’s a subtle improvement. Deque’s original methods had conflicting documentation about what happens with empty deques—some threw exceptions, others returned null. SequencedCollection standardizes this: getFirst() and getLast() throw NoSuchElementException on empty collections, matching the behavior of first() and last() in SortedSet.

Deque<String> deque = new ArrayDeque<>();

// Both interfaces, same methods, consistent behavior
deque.addFirst("item"); // From Deque
deque.getFirst(); // From SequencedCollection
deque.reversed().addFirst("item2"); // Adds to end via reversed view

Which Collections Are Sequenced?

Not every collection has a defined order. HashSet and HashMap explicitly don’t guarantee encounter order. The new interfaces only apply to collections where order matters:

SequencedCollection implementations:

  • List (ArrayList, LinkedList, Vector, Stack)
  • Deque (ArrayDeque, LinkedList)
  • LinkedHashSet
  • SortedSet (TreeSet and subclasses)

SequencedMap implementations:

  • LinkedHashMap
  • SortedMap (TreeMap and subclasses)

Notably absent: HashSet, HashMap, and EnumMap. These don’t implement the sequenced interfaces because they don’t guarantee consistent ordering.

Sorted Collections: Natural Sequencing

SortedSet and SortedMap already had ordering—they sort elements by natural order or a custom comparator. These now extend the sequenced interfaces, providing uniform access methods alongside their existing first(), last(), and subset operations.

SortedSet<Integer> sortedSet = new TreeSet<>(List.of(3, 1, 4, 1, 5, 9, 2, 6));

// Both work - sorted interface methods
int smallest = sortedSet.first(); // 1

// And sequenced interface methods 
int alsoSmallest = sortedSet.getFirst(); // 1
int largest = sortedSet.getLast(); // 9

// Reversed view maintains sorted properties
SequencedSet<Integer> descending = sortedSet.reversed();
System.out.println(descending); // [9, 6, 5, 4, 3, 2, 1]

The reversed view of a SortedSet is still sorted—just in reverse order. This maintains the collection’s invariants while providing bidirectional access.

Edge Cases and Exceptions

The API carefully defines failure behavior. Attempting to access elements from an empty collection throws NoSuchElementException:

List<String> emptyList = new ArrayList<>();

try {
 emptyList.getFirst(); // Throws NoSuchElementException
} catch (NoSuchElementException e) {
 System.out.println("List is empty");
}

This differs from get(0) which throws IndexOutOfBoundsException. The distinction is semantic—NoSuchElementException indicates the absence of an element, while IndexOutOfBoundsException indicates an invalid index. Both signal errors, but with different meanings.

For unmodifiable collections, mutation operations throw UnsupportedOperationException:

List<String> immutable = List.of("A", "B", "C");

immutable.addFirst("Z"); // UnsupportedOperationException
immutable.reversed().removeFirst(); // Also throws

The reversed view inherits mutability characteristics from the original collection. If the original is unmodifiable, so is the view.

Real-World Impact: Cleaner APIs

The biggest benefit isn’t in application code—it’s in library and framework design. APIs can now accept SequencedCollection instead of List when they need ordered access but don’t require random access or indexing.

// Before - overspecified requirement
public void processInOrder(List<Task> tasks) {
 Task first = tasks.get(0);
 Task last = tasks.get(tasks.size() - 1);
 // Process tasks
}

// After - more flexible contract
public void processInOrder(SequencedCollection<Task> tasks) {
 Task first = tasks.getFirst();
 Task last = tasks.getLast();
 // Process tasks - now works with Deque, LinkedHashSet, etc.
}

This makes APIs more flexible without sacrificing type safety. Callers can pass any ordered collection, not just lists.

Performance Characteristics

Most sequenced operations are constant-time O(1) for well-designed collections:

  • ArrayList: getFirst() is O(1), getLast() is O(1), but addFirst() is O(n) due to shifting
  • LinkedList: All sequenced operations are O(1)
  • ArrayDeque: All sequenced operations are O(1)
  • TreeSet: getFirst() and getLast() are O(log n), removals are O(log n)

The reversed view itself is always O(1) to create, but operations on the view have the same complexity as operations on the original collection.

Understanding these characteristics helps you choose the right collection. Need frequent additions at both ends? Use ArrayDeque or LinkedList, not ArrayList.

Migration Path: Backward Compatibility

The JDK team designed these interfaces with full backward compatibility. All existing collection implementations gained the new methods through default interface implementations where possible, or through explicit additions.

Your existing code continues to work unchanged. If you have a method accepting List, it still accepts List—which now happens to also implement SequencedCollection.

// Old code still works
public void oldMethod(List<String> list) {
 String first = list.get(0); // Still valid
}

// New code can be more flexible
public void newMethod(SequencedCollection<String> collection) {
 String first = collection.getFirst(); // More general
}

The migration path is opt-in. You can adopt sequenced collections gradually, starting with new APIs and migrating existing ones as needed.

Beyond Java: Language Evolution

Sequenced Collections represent a pattern seen across language evolution: filling gaps that seemed obvious in retrospect. Python’s collections had consistent indexing from day one. C#’s LINQ provides uniform access across collection types. Rust’s Iterator trait gives every collection a common vocabulary.

Java took longer to get here, but the result is thoughtfully designed and fully compatible with 25 years of existing collections code. That’s no small feat for a language with Java’s backward-compatibility requirements.

The lesson for language designers? Start with strong abstractions. The collection hierarchy should capture semantic properties—ordered vs unordered, indexed vs sequential, mutable vs immutable—from the beginning. Retrofitting is possible but messy.

What’s Still Missing

Sequenced Collections solve the “first and last element” problem elegantly, but some operations remain awkward. Want the second element? You still need list.get(1) for lists or collection.stream().skip(1).findFirst() for other sequenced collections.

There’s no standard way to slice a sequenced collection (get elements 5 through 10). List has subList(), but SequencedCollection doesn’t mandate this. Each implementation might offer different capabilities.

The API also doesn’t address concurrent collections. ConcurrentLinkedQueue and ConcurrentLinkedDeque don’t implement SequencedCollection because their ordering guarantees are weaker in concurrent scenarios. This leaves a gap for concurrent ordered collections.

These are minor quibbles. The API delivers exactly what it promises: uniform access to the endpoints of ordered collections. Future JEPs might address additional scenarios, but the foundation is solid.

Useful Links

Official Documentation:

Deep Dives and Analysis:

Migration and Best Practices:

Community Discussions:

Related Java 21 Features:

Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy

Thank you!

We will contact you soon.

👁 Photo of Eleftheria Drosopoulou
Eleftheria Drosopoulou
November 7th, 2025Last Updated: October 31st, 2025
0 1,576 7 minutes read

Eleftheria Drosopoulou

Eleftheria is an Experienced Business Analyst with a robust background in the computer software industry. Proficient in Computer Software Training, Digital Marketing, HTML Scripting, and Microsoft Office, they bring a wealth of technical skills to the table. Additionally, she has a love for writing articles on various tech subjects, showcasing a talent for translating complex concepts into accessible content.
Subscribe

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button
Close
wpDiscuz