Collections Framework

List, Set y Map — interfaces, implementaciones y criterios de elección

List: secuencia ordenada

List es la colección más usada. Mantiene el orden de inserción y permite duplicados. ArrayList (basada en array interno) es la implementación predeterminada — acceso por índice en O(1). LinkedList es mejor cuando se insertan o eliminan elementos frecuentemente en el medio de la lista.

import java.util.*;

// List: secuencia ordenada con duplicados permitidos
List<String> nombres = new ArrayList<>();  // implementación más usada: array redimensionable
nombres.add("Ana");
nombres.add("Luis");
nombres.add("Ana");      // duplicados permitidos
nombres.add(1, "Eva");   // insertar en posición

String primero = nombres.get(0);    // "Ana"
nombres.set(0, "Carlos");           // reemplazar
nombres.remove("Luis");             // por valor
nombres.remove(0);                  // por índice
int pos = nombres.indexOf("Ana");   // -1 si no existe
boolean existe = nombres.contains("Eva");

// LinkedList: mejor rendimiento al insertar/eliminar en el medio
List<String> lista = new LinkedList<>();
// ArrayList mejor para acceso por índice; LinkedList para inserciones frecuentes en medio

Set: sin duplicados

Set garantiza que no haya duplicados. La decisión de si dos objetos son iguales depende de equals() y hashCode() — si no los sobreescribes en tu clase, dos instancias distintas nunca se considerarán duplicadas.

// Set: sin duplicados, sin orden garantizado (salvo implementaciones concretas)

// HashSet: O(1) para add/contains/remove — sin orden
Set<String> ciudades = new HashSet<>();
ciudades.add("Madrid");
ciudades.add("Barcelona");
ciudades.add("Madrid"); // ignorado: ya existe
// ciudades.size() == 2

// LinkedHashSet: mantiene el orden de inserción
Set<String> ordenadas = new LinkedHashSet<>();
ordenadas.add("Z"); ordenadas.add("A"); ordenadas.add("M");
// iteración: Z, A, M

// TreeSet: orden natural (Comparable) o Comparator personalizado
Set<String> alfabetico = new TreeSet<>();
alfabetico.add("Z"); alfabetico.add("A"); alfabetico.add("M");
// iteración: A, M, Z — O(log n) para operaciones

Map: pares clave-valor

Map almacena pares clave-valor con claves únicas. HashMap es la implementación habitual — acceso en O(1). Las claves deben implementar correctamente equals() y hashCode().

// Map: pares clave-valor, claves únicas
Map<String, Integer> edades = new HashMap<>();
edades.put("Ana", 30);
edades.put("Luis", 25);
edades.put("Ana", 31); // reemplaza el valor anterior

Integer edad = edades.get("Ana");       // 31
Integer noExiste = edades.get("Pedro"); // null si no existe
int conDefault = edades.getOrDefault("Pedro", 0); // 0 si no existe

edades.containsKey("Luis");   // true
edades.containsValue(25);     // true
edades.remove("Luis");

// Iterar sobre el mapa
for (Map.Entry<String, Integer> entrada : edades.entrySet()) {
    System.out.println(entrada.getKey() + " → " + entrada.getValue());
}

// LinkedHashMap: mantiene orden de inserción
// TreeMap: orden natural de claves

Criterios de elección

// Criterios de elección:
//
// ¿Necesitas índices?
//   → List (ArrayList para acceso, LinkedList para inserciones frecuentes en el medio)
//
// ¿Solo necesitas saber si algo existe (sin duplicados)?
//   → Set (HashSet más rápido, TreeSet si necesitas orden, LinkedHashSet para orden de inserción)
//
// ¿Necesitas asociar claves con valores?
//   → Map (HashMap más rápido, TreeMap si necesitas orden por clave)
//
// Capacidades de rendimiento:
//   HashMap / HashSet: O(1) amortizado para get/put/contains
//   TreeMap / TreeSet: O(log n) para get/put/contains, pero ordenado
//   ArrayList: O(1) para get(idx), O(n) para insertar en el medio
//   LinkedList: O(n) para get(idx), O(1) para insertar si tienes el iterador

// Inicialización conveniente (Java 8 — inmutables en Java 9+):
List<String> inmutable = Collections.unmodifiableList(Arrays.asList("a", "b", "c"));
Map<String, Integer> mapa = new HashMap<String, Integer>() {{
    put("a", 1); put("b", 2);
}}; // doble-brace initialization: funciona pero crea una subclase anónima — evitar
Programa contra interfaces, no implementaciones. Declara las variables como List<String>, no ArrayList<String>. Así puedes cambiar la implementación sin tocar el código que la usa. La única excepción es cuando necesitas una operación específica de la implementación concreta.

Siguiente apartado → E/S con streams: InputStream/OutputStream y try-with-resources

Índice de la sección

Índice del curso