SequencedCollection, SequencedSet y SequencedMap (Java 21)

Avanzado
Java
Java
Actualizado: 27/04/2026

Sequenced Collections Java 21

flowchart TB
    Seq[SequencedCollection 21] --> List[List]
    Seq --> SeqSet[SequencedSet]
    Seq --> SeqMap[SequencedMap]
    SeqSet --> LHS[LinkedHashSet]
    SeqSet --> TS[TreeSet]
    SeqMap --> LHM[LinkedHashMap]
    SeqMap --> TM[TreeMap]
    Seq --> First[getFirst getLast]
    Seq --> Add[addFirst addLast]
    Seq --> Rev[reversed vista invertida]

El problema histórico del Collections Framework

El Collections Framework, diseñado en Java 2 (1998), tenía una inconsistencia notable: muchas colecciones tienen un orden de encuentro bien definido, pero las operaciones para acceder al primer y último elemento eran distintas según la clase concreta.

// Para obtener el primer elemento: una forma distinta en cada clase
list.get(0); // ArrayList, LinkedList
linkedHashSet.iterator().next(); // LinkedHashSet: sin método directo
treeSet.first(); // TreeSet: método específico
deque.peekFirst(); // Deque: método específico

// Para el último
list.get(list.size() - 1);
treeSet.last();
deque.peekLast();

Añadir al principio de una lista también era incómodo:

list.add(0, elemento); // ArrayList: funciona pero O(n)
linkedList.addFirst(elemento); // LinkedList tiene el método
linkedHashSet.addFirst(...); // no existía

Java 21 introduce tres nuevas interfaces: SequencedCollection, SequencedSet y SequencedMap: que unifican estas operaciones.

SequencedCollection<E>

La interfaz SequencedCollection<E> extiende Collection<E> y añade:

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

Implementaciones existentes que la adoptan:

  • ArrayList
  • LinkedList (ya tenía addFirst/addLast)
  • ArrayDeque
  • Cualquier Deque implementa ahora SequencedCollection

Ejemplo:

List<String> lista = new ArrayList<>(List.of("b", "c", "d"));

lista.addFirst("a"); // [a, b, c, d]
lista.addLast("e"); // [a, b, c, d, e]

String primero = lista.getFirst(); // "a"
String ultimo = lista.getLast(); // "e"

lista.removeFirst(); // [b, c, d, e]
lista.removeLast(); // [b, c, d]

SequencedCollection<String> reversed = lista.reversed(); // vista: [d, c, b]

Vista invertida

reversed() devuelve una vista (no una copia) del orden opuesto. Modificar la vista modifica el original:

List<Integer> original = new ArrayList<>(List.of(1, 2, 3, 4, 5));
SequencedCollection<Integer> reverso = original.reversed();

// Recorrer en orden inverso sin copiar
for (int n : reverso) {
    System.out.println(n); // 5, 4, 3, 2, 1
}

// Modificar a través de la vista
reverso.addFirst(0); // añade al "primero" del reverso = al FINAL del original
// original ahora: [1, 2, 3, 4, 5, 0]

SequencedSet<E>

Extiende Set<E> y SequencedCollection<E>. Proporciona los mismos métodos pero sobre conjuntos con orden definido.

Implementaciones:

  • LinkedHashSet: orden de inserción
  • TreeSet: orden natural o por comparator
SequencedSet<String> set = new LinkedHashSet<>();
set.addLast("a");
set.addLast("b");
set.addLast("c");
set.addFirst("zero");
System.out.println(set); // [zero, a, b, c]

set.reversed().forEach(System.out::println); // c, b, a, zero

Para TreeSet, addFirst/addLast lanzan UnsupportedOperationException porque el orden lo determina el comparador (no puedes "forzar" la posición). Pero getFirst, getLast, removeFirst, removeLast y reversed sí funcionan.

SequencedMap<K, V>

Extiende Map<K, V> añadiendo:

interface SequencedMap<K, V> extends Map<K, V> {
    V putFirst(K key, V value);
    V putLast(K key, V value);
    Map.Entry<K, V> firstEntry();
    Map.Entry<K, V> lastEntry();
    Map.Entry<K, V> pollFirstEntry();
    Map.Entry<K, V> pollLastEntry();
    SequencedMap<K, V> reversed();
    SequencedSet<K> sequencedKeySet();
    SequencedCollection<V> sequencedValues();
    SequencedSet<Map.Entry<K, V>> sequencedEntrySet();
}

Implementaciones:

  • LinkedHashMap
  • TreeMap
SequencedMap<String, Integer> mapa = new LinkedHashMap<>();
mapa.put("a", 1);
mapa.put("b", 2);
mapa.put("c", 3);
mapa.putFirst("zero", 0); // [zero=0, a=1, b=2, c=3]

Map.Entry<String, Integer> primera = mapa.firstEntry(); // zero=0
Map.Entry<String, Integer> ultima = mapa.lastEntry(); // c=3

// Vista invertida
SequencedMap<String, Integer> reverso = mapa.reversed();
reverso.forEach((k, v) -> System.out.println(k + "=" + v));
// c=3, b=2, a=1, zero=0

Casos de uso reales

Cola LIFO / stack con LinkedList

Antes, Stack era la clase obsoleta. Con SequencedCollection tienes una API clara:

SequencedCollection<String> pila = new LinkedList<>();
pila.addFirst("a"); // push
pila.addFirst("b");
pila.addFirst("c");
String top = pila.getFirst(); // peek: "c"
String pop = pila.removeFirst(); // pop: "c"

Cache LRU con LinkedHashMap

LinkedHashMap mantiene orden de inserción; con reversed() y acceso a primer/último es trivial implementar una cache LRU:

SequencedMap<String, Producto> cache = new LinkedHashMap<>();

void accesar(String clave, Producto p) {
    cache.remove(clave); // quitar si existía
    cache.putLast(clave, p); // ponerlo como más reciente
    if (cache.size() > MAX) {
        cache.pollFirstEntry(); // eliminar el menos reciente
    }
}

Iterar en orden inverso sin copiar

Antes de Java 21, para recorrer una lista al revés se creaba una copia invertida o se usaba un ListIterator manualmente:

// Antes
for (int i = lista.size() - 1; i >= 0; i--) {
    process(lista.get(i));
}

// Con Java 21
for (var x : lista.reversed()) {
    process(x);
}

TreeMap/TreeSet invertidos

SequencedMap<Integer, String> scores = new TreeMap<>();
scores.put(85, "Ana");
scores.put(92, "Bob");
scores.put(78, "Carmen");

// Top 3 scores (orden descendente)
scores.reversed().entrySet().stream()
.limit(3)
.forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));

Qué interfaces implementan qué

| Clase | SequencedCollection | SequencedSet | SequencedMap | |-------|:---:|:---:|:---:| | ArrayList | ✅ | | | | LinkedList | ✅ | | | | ArrayDeque | ✅ | | | | PriorityQueue | | | | | HashSet | | | | | LinkedHashSet | ✅ | ✅ | | | TreeSet | ✅ | ✅ | | | HashMap | | | | | LinkedHashMap | | | ✅ | | TreeMap | | | ✅ |

Nota: HashSet, HashMap y PriorityQueue no implementan las interfaces secuenciadas porque no tienen orden definido (HashSet/HashMap) o el orden depende del estado interno (PriorityQueue).

Caso B2B: cola de eventos en banca y telco

En entornos bancarios donde vuestro equipo procesa una cola de eventos transaccionales con orden estricto de llegada (por ejemplo, una pasarela SEPA Instant que ingresa cargos y abonos en milésimas de segundo), las colecciones secuenciadas eliminan el código defensivo que antes mezclaba Deque, LinkedList y comprobaciones de tamaño. La organización gana legibilidad y reduce la superficie para fallos por confusión entre peek() y peekFirst() durante guardias de incidencias.

En telco, la implementación de un buffer LRU para sesiones de usuario móvil (cache de tokens OAuth en pasarelas API que reciben centenares de miles de peticiones por segundo) se simplifica con LinkedHashMap y pollFirstEntry(). Antes de Java 21, equipos enteros mantenían wrappers propios sobre LinkedHashMap con removeEldestEntry; ahora la misma lógica cabe en cinco líneas y queda explícita en revisiones de código.

En retail, sistemas de fidelización que muestran los últimos cinco productos visitados por el cliente sobre LinkedHashSet<String> aprovechan set.reversed().stream().limit(5) sin reconstruir colecciones intermedias, lo que importa cuando el catálogo recibe rachas de tráfico durante campañas de Black Friday.

Versiones y disponibilidad

SequencedCollection, SequencedSet y SequencedMap están disponibles desde Java 21 LTS (septiembre 2023) y forman parte del JDK 21+. Java 25 LTS (septiembre 2025) las mantiene sin cambios incompatibles. Son interfaces estables de la plataforma, fuera de cualquier --enable-preview. El JEP de referencia es el JEP 431 (Sequenced Collections), aceptado y entregado en JDK 21.

Para vuestra organización, esto implica que no hay flag de compilación ni dependencia adicional: basta con compilar contra --release 21 o superior. En proyectos con base instalada en Java 17 LTS (frecuente en banca y AAPP por ciclos de migración), el uso de estas interfaces obliga a elevar el bytecode mínimo, decisión que conviene coordinar con la oficina de arquitectura.

Anti-patrones y pitfalls

Asumir que addFirst siempre funciona en Set. En TreeSet, las inserciones por posición lanzan UnsupportedOperationException porque el orden lo determina el Comparator. Si vuestro código recibe un SequencedSet genérico desde otra capa, conviene documentar la implementación esperada o capturar la excepción en pruebas de contrato.

Confundir vista con copia. reversed() devuelve una vista en vivo: cualquier mutación se refleja en el original. En código concurrente sin sincronización externa esto provoca ConcurrentModificationException con la misma probabilidad que iterar el original. Si necesitáis un snapshot, copiad explícitamente con new ArrayList<>(coleccion.reversed()).

Sustituir Stack por LinkedList sin medir. Aunque LinkedList ofrece la API correcta para una pila LIFO, su rendimiento en cachés modernas es inferior al de ArrayDeque por la fragmentación de memoria de los nodos enlazados. Para pilas calientes en bucles internos, ArrayDeque con addFirst/removeFirst rinde mejor. Esto se nota en cargas de microservicios con SLA por debajo de 50 ms.

Olvidar que pollFirstEntry() modifica el mapa. Algunos equipos lo usan para "consultar" el primer elemento y se sorprenden cuando desaparece de la cache. Para inspección sin efectos laterales, usad firstEntry().

Comparativa con alternativas

Frente a Guava (Iterables.getFirst, Iterables.getLast), las interfaces secuenciadas evitan la dependencia externa y operan en O(1) sobre estructuras enlazadas, mientras que Iterables.getLast puede recorrer toda la colección si no detecta List.

Frente a Apache Commons Collections (CollectionUtils, ListUtils.reverse), Java 21 ofrece la misma capacidad sin librerías adicionales y con la garantía de mantenimiento del JDK durante todo el ciclo LTS (al menos hasta 2031 para Java 21).

Frente a Kotlin (first(), last(), reversed() en Collection), Java 21 cierra parte de la brecha de ergonomía sin necesidad de cambiar de lenguaje, lo cual conviene a equipos con base de código histórica en Java que no quieren adoptar Kotlin solo por sintaxis de colecciones.

Documentación oficial

La especificación canónica está en la documentación de Oracle de la API Java SE: paquete java.util, interfaces SequencedCollection, SequencedSet y SequencedMap. El JEP 431 detalla la motivación y la lista exhaustiva de métodos por implementación.

Resumen

Las interfaces secuenciadas de Java 21 completan el Collections Framework. Ya no hay que recordar qué clase tiene qué método para acceder al primer o último elemento: todas las que tienen orden definido comparten la misma API uniforme.

Para código profesional Java 21+, estos métodos son la forma canónica de operar con orden:

  • addFirst / addLast: inserción con posición
  • getFirst / getLast: acceso directo
  • removeFirst / removeLast: extracción
  • reversed(): vista invertida

Aprovechad estas interfaces siempre que vuestro equipo escriba código nuevo sobre listas, deques, sets y maps con orden definido. En proyectos B2B con presión por mantenibilidad, la consistencia de la API reduce el coste de revisión y el riesgo de bugs por confusión entre métodos heredados de cada implementación.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Java es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Java

Explora más contenido relacionado con Java y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Conocer las interfaces SequencedCollection, SequencedSet y SequencedMap. Usar los nuevos métodos uniformes: addFirst, addLast, getFirst, getLast, removeFirst, removeLast. Aplicar reversed() para obtener vistas invertidas de colecciones. Integrar estas interfaces con implementaciones existentes (ArrayList, LinkedList, LinkedHashSet, LinkedHashMap, TreeSet, TreeMap). Entender por qué este refactor faltaba en el Collections Framework.