Sequenced Collections

Quick summary:

  • Welcomes a new interface, SequencedCollection, to the Collections-family.
  • With a SequencedCollection, we can assume a specific order, thus we have methods that let us access, add or remove elements at both ends of the collection, without the overhead and tight coupling of more specific List or Queue implementations.
  • Released in Java 21 (2023).

Key Features

  • Order Maintenance: Maintains elements in the order they are added, or in a specified sequence.
  • Performance Optimization: Provides better time and space performance compared to traditional List implementations, especially where frequent random access is not required.
  • Usability: Offers a simple API that fits seamlessly into the existing Collections Framework, making it easy for developers to adopt and integrate into their applications.

Example

Before Java 21:

Before, the Collections family hierarchy looked like this:

And, if we wanted to rely on some kind of defined order, we had to go down to a specific interface like Queue, List, or even some Sets like SortedSet and LinkedHashSet.

Even then, we are faced with annoyances like inconsistent ways to accessing the first and last element:

        // Accessing the first element is inconsistent!
        first = list.get(0);
        first = deque.getFirst();
        first = sortedSet.first();
        first = linkedHashSet.iterator().next();
        // We can't use a more general method in the super-interface
        first = collection.getFirst(); // ❌

After Java 17:

The new SequencedCollection wedges itself into the existing Collections-family, meaning integrating it into existing code is seamless.

The new SequencedCollection offers a uniform way to access, add and remove elements (at both ends of the collection).

        // Accessing the first element is easy thanks to SequencedCollection
        first = list.getFirst();
        first = deque.getFirst();
        first = sortedSet.getFirst();
        first = linkedHashSet.getFirst();
        // We can now use a more general method in the super-interface
        first = sequencedCollection.getFirst(); // ✅

New Methods

  • We can add to the front or the back of the collection is addFirst(E) and addLast(E).
  • We can remove from either end with removeFirst() and removeLast().
  • Retrieving from either end is easy thanks to getFirst() and getLast().
  • It’s even possible to have a reverse-ordered view of the collection with reversed().

What about Maps?

  • Don’t worry! Maps get the sequenced treatment too!
  • Any implementation of Map that maintains a defined order now implements SequencedMap.
  • We get analogous methods to manage both ends of the map, plus a way to get a reverse-ordered view.

👩‍💻 Hands-on Demo: Sequenced Collections

  1. In a PrimeNumsApp-class, write a getRandomSequencedCollectionOfPrimes-method that accepts a variable amount of Integer arguments, and returns a SequencedCollection with a randomly chosen implementation (e.g. ArrayList, ArrayDeque and LinkedHashSet).
  2. In the main-method, use your method to declare and initialise a sequenced collection, passing some prime numbers.
  3. Try out addFirst(), addLast() and removeLast() on your sequenced collection.
  4. In a print-statement, use the getFirst(), getLast() and reversed() methods (see screenshot).

Solutions

🕵️‍♂️ Click here to reveal the solutions
public class PrimeNumsApp {
    public static void main(String[] args) {
        SequencedCollection<Integer> primes = getRandomSequencedCollectionOfPrimes(3, 5, 7);

        primes.addFirst(2);
        primes.addLast(11);
        primes.addLast(13);

        primes.removeLast();

        System.out.printf("""
                %s of primes: %s
                        First element: %s
                        Last element: %s
                        Reversed: %s
                """, primes.getClass().getSimpleName(), primes, primes.getFirst(), primes.getLast(), primes.reversed());
    }

    private static SequencedCollection<Integer> getRandomSequencedCollectionOfPrimes(Integer... primes) {
        return switch (new Random().nextInt(16)) {
            case 0 -> new ArrayList<>(Arrays.asList(primes));
            case 1 -> new ArrayDeque<>(Arrays.asList(primes));
            case 2 -> new LinkedHashSet<>(Arrays.asList(primes));
            default -> throw new IllegalStateException("Unknown random number");
        };
    }
}

Summary

  • Introduction of SequencedCollection: Java 21 adds SequencedCollection to the Collections framework, ensuring ordered maintenance of elements with efficient operations to manage both ends of the collection.
  • Performance and Usability: Offers superior performance over traditional List implementations and provides a straightforward API, integrating seamlessly into existing collections without the need for specific List or Queue functionalities.
  • Uniform Access Methods: Simplifies code with consistent methods like getFirst(), getLast(), addFirst(), and addLast() for accessing and modifying elements, which eliminates the inconsistencies found in older collection types.
  • Extension to Maps: Introduces SequencedMap, applying ordered capabilities to map implementations with additional features for reverse viewing and managing endpoints, enhancing map usability significantly.