Back to Advanced Topics
Q33Question 33 of 41

What are Sequenced Collections (Java 21), and what gap did they fill?

Key Concepts
  • SequencedCollection, SequencedSet, and SequencedMap are three new interfaces in Java 21 that unify first, last, and reversed access for any ordered collection.
  • They add getFirst(), getLast(), addFirst(), addLast(), removeFirst(), removeLast(), and reversed() to the collection hierarchy.
  • List, Deque, LinkedHashSet, LinkedHashMap, and the sorted-set and sorted-map families were retrofitted to implement these interfaces.
  • The reversed() method returns a view, not a copy, so iterating it walks the original backwards and changes write through.
  • getFirst() and getLast() throw NoSuchElementException on an empty collection.
  • addFirst() and addLast() on a sorted set or map throw UnsupportedOperationException because position cannot override sort order.
  • The feature adds no new data structures; it is purely a unifying API that removes index arithmetic and iterator boilerplate.

Definition

Sequenced Collections give every ordered collection one uniform API for first, last, and reversed access, filling the gap where getting the first or last element and reversing differed inconsistently across List, Deque, and ordered sets and maps.
  • SequencedCollection – Base interface adding getFirst(), getLast(), addFirst(), addLast(), removeFirst(), removeLast(), and reversed() for any collection with a defined encounter order.
  • SequencedSet – Extends SequencedCollection for sets with unique elements, refining reversed() to return a SequencedSet.
  • SequencedMap – Map counterpart adding firstEntry(), lastEntry(), pollFirstEntry(), pollLastEntry(), putFirst(), putLast(), and reversed().
  • Retrofitted – List, Deque, LinkedHashSet, LinkedHashMap, and the NavigableSet/NavigableMap families now implement these interfaces.
  • reversed() view – Returns a view, not a copy, so iterating walks backwards and changes write through to the original.

Imagine National Bank of India keeps several ordered queues: a line of customers at the counter, a ledger of transactions in date order, and a list of recent logins. Before Java 21, asking "who is first" and "who is last" worked differently for each: for one you called a method, for another you did size minus one, and for a set you had to iterate the whole thing just to reach the end. Sequenced Collections are like a single standard rule posted on every ordered queue: "to see the front, say getFirst; to see the back, say getLast; to read it backwards, say reversed."

Understanding the Concepts

What gap did Sequenced Collections fill?

Before Java 21, order was a concept scattered across types with no shared interface. A List gave its first element with get(0) and its last with get(size() - 1). A Deque had getFirst() and getLast(). A LinkedHashSet had a defined iteration order but no direct way to get the last element except iterating to the end. A LinkedHashMap and a sorted map had order too, but accessing the ends was inconsistent.

JAVA
package com.bank.service;

import java.util.*;

public class BeforeSequencedCollections {

    public void inconsistentAccess() {
        // List: index arithmetic
        List<String> accounts = List.of("SAV2001", "CUR3001", "SAV2002");
        String first = accounts.get(0);
        String last = accounts.get(accounts.size() - 1); // error-prone

        // Deque: different methods
        Deque<String> queue = new ArrayDeque<>(accounts);
        String dFirst = queue.getFirst();
        String dLast = queue.getLast();

        // LinkedHashSet: no direct last-element access
        Set<String> recent = new LinkedHashSet<>(accounts);
        String sFirst = recent.iterator().next();
        // No getLast()! Must iterate to the end
    }
}

What are the new interfaces and methods?

SequencedCollection is the base, adding uniform first and last access. SequencedSet extends it for unique elements. SequencedMap is the map counterpart.

JAVA
package com.bank.service;

import java.util.*;
import java.math.BigDecimal;

public class SequencedCollectionDemo {

    public void demonstrateNewMethods() {
        // SequencedCollection: getFirst, getLast, reversed
        List<String> transactions = new ArrayList<>(
            List.of("TXN-001", "TXN-002", "TXN-003")
        );
        String oldest = transactions.getFirst();  // TXN-001
        String newest = transactions.getLast();    // TXN-003

        // SequencedSet: unique elements with order
        SequencedSet<String> recentAccounts = new LinkedHashSet<>();
        recentAccounts.addFirst("SAV2001");
        recentAccounts.addLast("CUR3001");
        String lastUsed = recentAccounts.getLast(); // CUR3001

        // SequencedMap: first and last entries
        SequencedMap<String, BigDecimal> balances =
            new LinkedHashMap<>();
        balances.put("SAV2001", new BigDecimal("50000.00"));
        balances.put("CUR3001", new BigDecimal("120000.00"));
        balances.put("SAV2002", new BigDecimal("75000.00"));

        Map.Entry<String, BigDecimal> first =
            balances.firstEntry();
        Map.Entry<String, BigDecimal> last =
            balances.lastEntry();

        System.out.println("First: " + first);
        System.out.println("Last: " + last);
    }
}
TEXT
First: SAV2001=50000.00
Last: SAV2002=75000.00

How does the reversed() view work?

The reversed() method returns a view, not a copy. Iterating it walks the original in reverse, and for mutable collections, changes through the view write back to the original.

JAVA
package com.bank.service;

import java.util.*;
import java.math.BigDecimal;

public class ReversedViewDemo {

    public void newestFirstStatement() {
        List<String> transactions = new ArrayList<>(
            List.of("Deposit 10000", "Transfer 5000", "Withdrawal 2000")
        );

        // View, not a copy: walks backwards
        List<String> newestFirst = transactions.reversed();
        for (String txn : newestFirst) {
            System.out.println(txn);
        }
        // Output:
        // Withdrawal 2000
        // Transfer 5000
        // Deposit 10000

        // Changes through the view write back
        newestFirst.set(0, "Withdrawal 3000");
        System.out.println(transactions.getLast());
        // Prints: Withdrawal 3000
    }
}

Which existing types were retrofitted?

| Type | Now Implements |
|---|---|
| List | SequencedCollection |
| Deque | SequencedCollection |
| LinkedHashSet | SequencedSet |
| SortedSet / NavigableSet | SequencedSet |
| LinkedHashMap | SequencedMap |
| SortedMap / NavigableMap | SequencedMap |

Existing collections gained the new methods without changing their declared types.

What are the edge cases and exceptions?

getFirst() and getLast() throw NoSuchElementException on an empty collection. addFirst() and addLast() on a sorted set or map throw UnsupportedOperationException because you cannot force a position against the comparator's order.

JAVA
package com.bank.service;

import java.util.*;

public class SequencedEdgeCases {

    public void emptyCollectionAccess() {
        List<String> empty = new ArrayList<>();
        try {
            empty.getFirst(); // NoSuchElementException
        } catch (NoSuchElementException e) {
            System.out.println("Empty list: " + e.getMessage());
        }
    }

    public void sortedSetPositionConflict() {
        SequencedSet<String> sorted = new TreeSet<>();
        sorted.add("CUR3001");
        sorted.add("SAV2001");
        try {
            sorted.addFirst("ZZZ9999");
            // UnsupportedOperationException: cannot force position
        } catch (UnsupportedOperationException e) {
            System.out.println("Sorted set: " + e.getMessage());
        }
    }
}

How is this different from using get(0) and get(size-1)?

| Operation | Before Java 21 | Java 21+ |
|---|---|---|
| First element (List) | list.get(0) | list.getFirst() |
| Last element (List) | list.get(list.size() - 1) | list.getLast() |
| First element (Set) | set.iterator().next() | set.getFirst() |
| Last element (Set) | Iterate entire set | set.getLast() |
| Reverse iteration | Copy + Collections.reverse | collection.reversed() |
| First map entry | Iterate to find | map.firstEntry() |

What about SequencedMap-specific methods?

SequencedMap adds firstEntry(), lastEntry(), pollFirstEntry() (retrieves and removes), pollLastEntry(), putFirst(), putLast(), and sequencedKeySet(), sequencedValues(), sequencedEntrySet() for ordered views.

JAVA
package com.bank.service;

import java.util.*;
import java.math.BigDecimal;

public class SequencedMapDemo {

    public void bankingMapOperations() {
        SequencedMap<String, BigDecimal> recentDeposits =
            new LinkedHashMap<>();
        recentDeposits.put("TXN-001", new BigDecimal("10000.00"));
        recentDeposits.put("TXN-002", new BigDecimal("25000.00"));
        recentDeposits.put("TXN-003", new BigDecimal("50000.00"));

        // First and last entries
        System.out.println("Earliest: " + recentDeposits.firstEntry());
        System.out.println("Latest: " + recentDeposits.lastEntry());

        // Poll removes and returns
        Map.Entry<String, BigDecimal> removed =
            recentDeposits.pollFirstEntry();
        System.out.println("Removed: " + removed);
        System.out.println("Remaining: " + recentDeposits.size());
    }
}

What should a senior engineer know?

The feature adds no new data structures; it is purely a unifying API layered over existing ordered types. That is exactly why it is valuable: it removes a pile of index arithmetic and iterator boilerplate and makes ordered-collection code read the same everywhere. The reversed() view is a view, not a copy, which matters for performance with large collections.

Visual Overview

DIAGRAM
┌───────────────────────────────────────────────────────────┐
│        Sequenced Collection Interface Hierarchy            │
├───────────────────────────────────────────────────────────┤
│                                                           │
│              SequencedCollection                          │
│              ┌─────────────────────┐                      │
│              │ getFirst/getLast    │                      │
│              │ addFirst/addLast    │                      │
│              │ removeFirst/Last    │                      │
│              │ reversed()         │                      │
│              └──────┬──────┬──────┘                      │
│                     │      │                              │
│         ┌───────────┘      └───────────┐                  │
│         ▼                              ▼                  │
│   SequencedSet                  SequencedMap              │
│   ┌──────────────┐              ┌──────────────────┐      │
│   │ reversed()   │              │ firstEntry()     │      │
│   │  → Seq.Set   │              │ lastEntry()      │      │
│   └──────────────┘              │ putFirst/putLast │      │
│         │                       │ reversed()→Seq.Map│     │
│         │                       └──────────────────┘      │
│         ▼                              ▼                  │
│  List, Deque,                LinkedHashMap,               │
│  LinkedHashSet,              SortedMap,                   │
│  SortedSet,                  NavigableMap                 │
│  NavigableSet                                             │
└───────────────────────────────────────────────────────────┘

Key Takeaway

Sequenced Collections, added in Java 21, are three new interfaces that give any collection with a defined encounter order one uniform API for first and last access and for reversal. SequencedCollection adds getFirst(), getLast(), addFirst(), addLast(), removeFirst(), removeLast(), and reversed(). SequencedSet refines this for unique elements. SequencedMap adds firstEntry(), lastEntry(), pollFirstEntry(), pollLastEntry(), putFirst(), putLast(), and reversed().

The gap they fill is that before Java 21, order was scattered across types with no shared interface. A List gave its first element with get(0) and its last with get(size() - 1). A Deque had getFirst() and getLast(). A LinkedHashSet had a defined iteration order but no direct way to reach the last element except iterating all the way to the end. There was no common type that said "I am ordered and I have a first and a last."

These interfaces were retrofitted into existing types, so List and Deque implement SequencedCollection, LinkedHashSet and the sorted-set family implement SequencedSet, and LinkedHashMap and the sorted-map family implement SequencedMap, all without changing declared types. The reversed() method returns a view rather than a copy, so iterating it walks the original backwards and changes through the view write back.

In banking work at National Bank of India this cleaned up ordered-collection code. A customer's transaction list exposed the most recent transaction with getLast() and a newest-first statement view with reversed(), instead of size-minus-one and manual reverse loops. A LinkedHashSet of recently used accounts gained direct first and last access. The senior details are that getFirst() and getLast() throw NoSuchElementException on an empty collection, and addFirst() or addLast() on a sorted set or map throws UnsupportedOperationException because position cannot override comparator order.

Remember one sentence: Sequenced Collections unify first, last, and reversed access across all ordered collection types with getFirst(), getLast(), and reversed(), eliminating inconsistent index arithmetic and iterator boilerplate.

---

Share
Use ← → arrow keys to navigate