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 elementosE. 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 unaCollection(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:
- ¿Qué necesitan los datos? Ordenados con duplicados →
List. Sin duplicados →Set. Búsqueda por clave →Map. Productor/consumidor →QueueoDeque. - Dentro de la familia, ¿cuáles son los patrones de acceso? Acceso aleatorio por índice →
ArrayList. Mucho añadir/quitar al principio →LinkedListoArrayDeque. Iteración ordenada →TreeSet/TreeMap. Simplemente «solo necesito una bolsa rápida» →HashSet/HashMap.
Una chuleta aproximada para los caballos de batalla:
| Necesidad | Use | Notas |
|---|---|---|
| Lista redimensionable, acceso aleatorio rápido | ArrayList | La List por defecto. |
| Lista con inserción/eliminación muy frecuente en los extremos | ArrayDeque (como cola similar a lista) | Supera a LinkedList en la práctica. |
| Conjunto, contains/add/remove rápidos | HashSet | Sin garantía de orden. |
| Conjunto con iteración predecible | LinkedHashSet | Iteración en orden de inserción. |
| Conjunto ordenado | TreeSet | Operaciones O(log n). |
| Map, búsqueda rápida | HashMap | El Map por defecto. |
| Map con iteración predecible | LinkedHashMap | Útil para cachés (LRU). |
| Map ordenado | TreeMap | Claves en orden ordenado. |
| Cola FIFO | ArrayDeque | Más rápida que LinkedList. |
| Extraer siempre el menor | PriorityQueue | Respaldada 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 neededEl 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.
Fíjese en tres cosas en la salida:
- El
Setdescartó silenciosamente el duplicado"Effective Java". Ese es el contrato — no hace falta código extra de su parte. - La
Queuedevolvió los elementos en el orden en que se añadieron.offerencola,peekmira la cabeza sin quitarla,pollla quita y la devuelve. - El bucle
for-eachfunciona en todaCollection. Los maps se iteran víaentrySet(),keySet()ovalues()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
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?