W3docs

Visión general del Java Collections Framework

Un recorrido de alto nivel por el Java Collections Framework — interfaces, implementaciones y algoritmos en java.util.

Visión general del Java Collections Framework

Casi todo programa acaba conteniendo un grupo de cosas — pedidos esperando a enviarse, palabras en un documento, usuarios actualmente conectados, la fila de tareas pendientes que un hilo trabajador recogerá a continuación. La respuesta de Java a «dónde pongo ese grupo» es el Collections Framework: un conjunto coordinado de interfaces en java.util, más de una docena de implementaciones detrás de ellas, y una única clase utilitaria de algoritmos (Collections) que trabaja contra las interfaces en lugar de contra cualquier implementación específica. Este capítulo es el mapa — qué hay en el framework, por qué tiene la forma que tiene y a qué familia recurre en cada situación. Cada capítulo de esta parte es una inmersión en una de las casillas de ese mapa.

Por qué un framework, y no solo clases

Antes de Java 1.2 había clases para grupos (Vector, Hashtable, Stack) pero ninguna interfaz compartida — no podía escribir un método que tomara «cualquier lista» y aceptara las tres. El Collections Framework lo arregló separando qué es un grupo (la interfaz) de cómo se almacena (la implementación). La recompensa está a la vista cada día:

// You program against the interface, not the class.
List<String> names = new ArrayList<>();
// Later, swap in a different implementation without touching the rest of the code:
List<String> names = new LinkedList<>();

Cada método que llama sobre names está definido en List. La elección entre ArrayList y LinkedList es una decisión de rendimiento, no de API. También puede escribir métodos como void print(List<String> xs) una sola vez y pasarle cualquiera de las implementaciones.

Las cuatro interfaces raíz

El framework se organiza en torno a cuatro interfaces raíz. Elija aquella cuyo contrato se corresponda con lo que son sus datos:

  • Collection<E> — la raíz. Un grupo de elementos E. No se dice nada sobre orden, duplicados o indexación.
  • List<E> — una colección ordenada accesible por índice. Se permiten duplicados. Piense en «arreglo que crece» o «secuencia enlazada». ArrayList, LinkedList, Vector.
  • Set<E> — una colección que prohíbe los duplicados. Conjunto matemático. Puede estar ordenada o no. HashSet, LinkedHashSet, TreeSet.
  • Map<K, V> — búsqueda clave→valor. No es una Collection (sus elementos son entradas, no valores únicos), pero vive en el framework. HashMap, LinkedHashMap, TreeMap.

Otras dos se sitúan junto a List y Set como especializaciones de Collection:

  • Queue<E> — una colección de «siguiente en la fila». FIFO por defecto. LinkedList, ArrayDeque, PriorityQueue.
  • Deque<E> — una cola de doble extremo. Añadir y quitar por cualquier extremo. ArrayDeque, LinkedList.

Cada clase de colección de la biblioteca estándar implementa al menos una de estas.

Elija la familia por comportamiento, luego la clase por coste

La decisión tiene dos capas:

  1. ¿Qué necesitan los datos? Ordenados con duplicados → List. Sin duplicados → Set. Búsqueda por clave → Map. Productor/consumidor → Queue o Deque.
  2. Dentro de la familia, ¿cuáles son los patrones de acceso? Acceso aleatorio por índice → ArrayList. Mucho añadir/quitar al principio → LinkedList o ArrayDeque. Iteración ordenada → TreeSet / TreeMap. Simplemente «solo necesito una bolsa rápida» → HashSet / HashMap.

Una chuleta aproximada para los caballos de batalla:

NecesidadUseNotas
Lista redimensionable, acceso aleatorio rápidoArrayListLa List por defecto.
Lista con inserción/eliminación muy frecuente en los extremosArrayDeque (como cola similar a lista)Supera a LinkedList en la práctica.
Conjunto, contains/add/remove rápidosHashSetSin garantía de orden.
Conjunto con iteración predecibleLinkedHashSetIteración en orden de inserción.
Conjunto ordenadoTreeSetOperaciones O(log n).
Map, búsqueda rápidaHashMapEl Map por defecto.
Map con iteración predecibleLinkedHashMapÚtil para cachés (LRU).
Map ordenadoTreeMapClaves en orden ordenado.
Cola FIFOArrayDequeMás rápida que LinkedList.
Extraer siempre el menorPriorityQueueRespaldada por un montículo.

Vector, Stack y Hashtable son las clases anteriores a la 1.2 — todavía en el JDK por compatibilidad hacia atrás, pero ya no la elección correcta en código nuevo. Sus reemplazos modernos se tratan en sus propios capítulos.

Los genéricos hacen las colecciones seguras en tipos

Cada interfaz y clase de colección es genérica. La parametriza con el tipo de elemento en el lugar de la declaración, y el compilador lo impone de ahí en adelante:

List<String> names = new ArrayList<>();
names.add("Ada");
names.add(42);          // ❌ compile error — Integer is not a String
String first = names.get(0);   // no cast needed

El diamante <> a la derecha le pide al compilador que infiera el tipo desde la izquierda — ahorra teclear sin perder la seguridad. La parte anterior del libro (Genéricos) es el reglamento detrás de todo esto; el resto de esta parte da por sentado que la leyó.

Los algoritmos viven en la clase Collections

Las operaciones que no están atadas a una implementación específica — ordenar, barajar, invertir, búsqueda binaria, min, max, frecuencia, envoltorios no modificables — viven en la clase utilitaria estática java.util.Collections. Note la s al final: Collection (la interfaz) es un elemento; Collections (la clase) es el conjunto de herramientas:

List<Integer> xs = new ArrayList<>(List.of(3, 1, 4, 1, 5, 9, 2, 6));
Collections.sort(xs);                    // mutates xs
int idx = Collections.binarySearch(xs, 5);
List<Integer> ro = Collections.unmodifiableList(xs);

Dedicamos tres capítulos a Collections cerca del final de esta parte — búsqueda, ordenación y envoltorios no modificables — porque es en la clase utilitaria donde ocurre mucho trabajo práctico.

Un primer recorrido completo

El programa de abajo escoge un representante de cada familia y muestra las operaciones que usará una y otra vez. No se preocupe todavía por entender cada línea — cada clase aquí tiene su propio capítulo. El objetivo es ver la forma del framework: los mismos nombres de método, los mismos patrones, el mismo modelo de iteración.

java— editable, runs on the server

Fíjese en tres cosas en la salida:

  1. El Set descartó silenciosamente el duplicado "Effective Java". Ese es el contrato — no hace falta código extra de su parte.
  2. La Queue devolvió los elementos en el orden en que se añadieron. offer encola, peek mira la cabeza sin quitarla, poll la quita y la devuelve.
  3. El bucle for-each funciona en toda Collection. Los maps se iteran vía entrySet(), keySet() o values() según lo que quiera.

Ese es el framework en miniatura.

Qué sigue

Ha visto el mapa. El resto de la Parte 11 recorre cada región. El punto de partida natural es la raíz de la que hereda toda colección — la propia interfaz Collection, donde métodos como add, remove, contains, size e iterator() se definen de una vez por todas.

Practice

Práctica

You need to store a group of words and quickly answer 'is this word in the group?' Duplicates are not interesting — `cat` either appears or doesn't. Which family of the Collections Framework is the natural fit?