W3docs

Interfaz List en Java

Colecciones ordenadas e indexables en Java con la interfaz List y sus operaciones principales.

List<E> es Collection<E> más dos compromisos adicionales: los elementos tienen un orden definido, y ese orden es accesible mediante un índice entero. Una vez que tienes orden e índice, toda una clase de métodos cobra sentido — get(i), set(i, x), indexOf(x), subList(from, to), ordenar, iterar en reversa. Este capítulo recorre el contrato; las implementaciones (ArrayList, LinkedList, Vector) vienen inmediatamente después con sus propias características de rendimiento.

Qué significa "ordenado" aquí

"Ordenado" en una List significa que el orden de inserción se preserva — el índice 0 es el primer elemento que insertaste, el índice size() - 1 es el último, y agregar un nuevo elemento al final no desplaza nada. No significa "ordenado alfabéticamente" — una lista mantiene el orden que tú produzcas. Si quieres iteración ordenada, debes llamar a Collections.sort(list) (que muta la lista) o usar TreeSet / TreeMap desde el principio. No confundas los dos conceptos.

Se permiten duplicados. [1, 1, 2, 1] es una List<Integer> perfectamente válida.

Los métodos que List añade sobre Collection

Todo lo que Collection declara sigue estando disponible — add, remove, contains, size, etc. List agrega además operaciones posicionales y conscientes del orden:

Acceso posicional

  • E get(int index) — elemento en index.
  • E set(int index, E element) — reemplaza y devuelve el valor anterior.
  • void add(int index, E element) — inserta (desplaza los elementos posteriores hacia la derecha).
  • E remove(int index) — elimina por posición (devuelve el elemento eliminado). Nota la sobrecarga con Objectlist.remove(1) llama a la versión int; list.remove(Integer.valueOf(1)) llama a la versión Object.

Búsqueda

  • int indexOf(Object o) — primera ocurrencia, o -1.
  • int lastIndexOf(Object o) — última ocurrencia, o -1.

Subvistas e iteración

  • List<E> subList(int fromIndex, int toIndex) — una vista en vivo de un rango. Modificarla modifica la lista subyacente (y viceversa). Semiabierto: [from, to).
  • ListIterator<E> listIterator() / listIterator(int index) — iterador que también puede recorrer hacia atrás, obtener el índice actual y hacer set / add en la posición del cursor. El capítulo ListIterator lo cubre en detalle.

Mutación masiva vinculada al orden

  • default void replaceAll(UnaryOperator<E> op) — aplica op a cada elemento en su lugar.
  • default void sort(Comparator<? super E> c) — ordena la lista usando c (o el orden natural si es null).
  • boolean addAll(int index, Collection<? extends E> c) — inserta una colección completa en index.

Fábricas (Java 9+)

  • List.of(...) — una lista no modificable con los elementos dados. Compacta y sin asignaciones adicionales para tamaños pequeños.
  • List.copyOf(Collection) — una instantánea no modificable de otra colección.

La igualdad en List es sensible al orden

Dos listas son iguales si y solo si tienen el mismo tamaño, en el mismo orden, con elementos iguales en cada índice. List.of(1, 2) no es igual a List.of(2, 1), aunque como Sets sí lo serían. Esa es una regla estricta del contrato de List — si comparas dos listas y obtienes false cuando "no deberías", verifica el orden primero.

subList es una vista, no una copia

Esto confunde a casi todos los aprendices en algún momento:

List<Integer> xs = new ArrayList<>(List.of(0, 1, 2, 3, 4, 5));
List<Integer> middle = xs.subList(2, 5);    // [2, 3, 4]
middle.set(0, 99);
System.out.println(xs);          // [0, 1, 99, 3, 4, 5]   — xs changed!
middle.clear();
System.out.println(xs);          // [0, 1, 5]            — gone from xs

subList devuelve una ventana en vivo. Las lecturas y escrituras pasan a través de la lista subyacente. Eso es muy útil para algoritmos en el lugar — borrar un rango, ordenar un rango, insertar un rango — pero también significa que no puedes mantener una referencia a subList y luego mutar el padre por cualquier otro camino. El Javadoc indica que los cambios estructurales en la lista subyacente fuera de la sublista "indefinen" el comportamiento de la sublista. En la práctica, ConcurrentModificationException en la siguiente llamada.

Si quieres un fragmento independiente, cópialo: new ArrayList<>(xs.subList(2, 5)).

Las dos sobrecargas de remove

Un error común:

List<Integer> nums = new ArrayList<>(List.of(10, 20, 30));
nums.remove(1);                       // removes index 1 → [10, 30]
nums.remove(Integer.valueOf(10));     // removes the value 10 → [30]

La sobrecarga int gana porque int es más específico que Integer. Si quieres "eliminar el valor 10", enciérralo explícitamente. Este es uno de los pocos lugares en el lenguaje donde las reglas de autoboxing y la resolución de sobrecargas entran en conflicto activo.

Ordenar en el lugar

list.sort(comparator) muta la lista. Pasa null para usar el orden natural de los elementos (su Comparable); de lo contrario, pasa un Comparator. Esta es la forma moderna — Collections.sort(list) todavía funciona y es idéntica, pero list.sort(...) es el método predeterminado de List:

List<String> names = new ArrayList<>(List.of("Linus", "Ada", "Grace"));
names.sort(null);                              // natural: ["Ada", "Grace", "Linus"]
names.sort(Comparator.comparingInt(String::length));  // shortest first

El capítulo Comparable / Comparator más adelante en esta parte es el manual de referencia sobre qué significa null y cómo construir comparadores para tus propios tipos.

Fábricas inmutables: cuándo add lanza una excepción

List.of(...), List.copyOf(...), y las listas devueltas por Collectors.toUnmodifiableList() son no modificables. Rechazan cada llamada mutante con UnsupportedOperationException. También rechazan elementos null. Son ideales para datos de solo lectura compartidos ampliamente:

List<String> CONSTANTS = List.of("red", "green", "blue");
CONSTANTS.add("yellow");    // throws UnsupportedOperationException

Si más adelante podrías querer mutar la lista, empieza con new ArrayList<>(List.of(...)).

Un ejemplo completo: todos los métodos específicos de List

El programa a continuación ejercita los métodos que List añade más allá de Collection. Observa cómo la mutación de subList se propaga, la trampa de la sobrecarga y la diferencia entre sort y replaceAll.

java— editable, runs on the server

Algunas conclusiones de la salida que debes recordar:

  • remove(1) eliminó 20 (el valor en el índice 1); remove(Integer.valueOf(10)) eliminó 10 por valor. Mismo nombre de método, dos trabajos distintos según el tipo estático del argumento.
  • Después de mid.clear(), la lista padre es [0, 1, 5]. La vista era ese rango — borrarla eliminó esos elementos del array subyacente.
  • replaceAll mantiene la lista con la misma longitud y reescribe cada elemento en su lugar; sort reorganiza lo que ya hay. Se combinan bien.

Qué sigue

Ahora conoces el contrato de List — qué está garantizado, qué muta qué, dónde están los peligros. Es momento de conocer la implementación que usarás el 90% del tiempo: el ArrayList respaldado por un array redimensionable. El mismo contrato, características de rendimiento específicas y algunas extras propias.

Práctica

Práctica
`xs` es `new ArrayList<>(List.of(10, 20, 30, 40))`. Llamas a `xs.subList(1, 3).clear()`. ¿Qué contiene `xs` después?
`xs` es `new ArrayList<>(List.of(10, 20, 30, 40))`. Llamas a `xs.subList(1, 3).clear()`. ¿Qué contiene `xs` después?
Was this page helpful?