Ir al contenido
wannacode

Listas

En resumen: Una lista es una colección que crece y se encoge sola; ArrayList guarda los elementos en orden como un array, y List<String> dice de qué tipo son.

Problema

El carrito está lleno

Estás escribiendo el carrito de compras de una tienda en línea, y guardas los productos en un array. Pero un array tiene un tamaño que no cambia. Cuando el cliente añade un cuarto producto, creas un array más grande y copias todo. Cuando quita un producto, queda un hueco:

String[] cart = { "Pan", "Leche", "Café" };
// Un cuarto producto: el array está lleno, así que creamos uno más grande y copiamos.
String[] bigger = new String[cart.length + 1];
for (int i = 0; i < cart.length; i++) {
bigger[i] = cart[i];
}
bigger[cart.length] = "Mantequilla";
cart = bigger;
// El cliente quita la leche.
cart[1] = null;
for (String item : cart) {
System.out.println(item);
}
Salida
Pan
null
Café
Mantequilla
Ahora cada bucle sobre el carrito tiene que saltarse null. Y el número de productos ya no es cart.length.

Es mucho código para algo que toda tienda hace todo el tiempo.

La idea

ArrayList: un array que se cuida solo

Java tiene una clase lista para esto: ArrayList. Guarda los elementos en un array, pero copia y desplaza sola. Una colección así se llama lista:

Main.java
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<String> cart = new ArrayList<>();
cart.add("Pan");
cart.add("Leche");
cart.add("Café");
cart.add("Mantequilla");
cart.remove("Leche");
System.out.println(cart);
System.out.println("Productos: " + cart.size());
}
}
Salida
[Pan, Café, Mantequilla]
Productos: 3
  • ArrayList está en el paquete java.util, así que el archivo empieza con un import, como con Scanner.
  • add añade al final, y remove quita y cierra el hueco: «Café» pasa al lugar de «Leche».
  • size() es cuántos elementos hay ahora mismo. No es length, porque una lista no es un array.
  • println imprime toda la lista entre corchetes.

Esto es lo que hace ArrayList por dentro. El número entre paréntesis es el tamaño inicial del array. Sin él, es 10. Aquí es 3, para que veas qué pasa cuando el array se llena:

ArrayList<String> cart = new ArrayList<>(3);
cart.add("Pan");
cart.add("Leche");
cart.add("Café");
cart.add("Mantequilla");
cart.remove("Leche");
System.out.println(cart);
System.out.println("Productos: " + cart.size());
Paso a paso, en palabras
  1. cart apunta a un objeto ArrayList. Dentro hay un array con 3 lugares y un contador size.
  2. add pone cada elemento en el primer lugar libre y aumenta size.
  3. El array está lleno. ArrayList crea uno nuevo con 4 lugares y copia en él los tres elementos.
  4. Ahora el objeto apunta al array nuevo, y el viejo ya no lo necesita nadie. «Mantequilla» va al lugar libre.
  5. remove quita «Leche», y todo lo que viene después se mueve un lugar a la izquierda. No queda ningún hueco.
  6. Desde fuera solo ves una lista de tres productos: ArrayList se encarga de copiar y de mover.

La idea

Qué va entre los corchetes angulares

ArrayList<String> significa «una lista de texto». El tipo entre los corchetes angulares se llama parámetro de tipo (generics). Le dice al compilador qué hay en la lista, y el compilador lo hace cumplir:

Main.java
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<String> cart = new ArrayList<>();
cart.add("Pan");
cart.add(5);
}
}
Error de compilación
Main.java:7: error: incompatible types: int cannot be converted to String
cart.add(5);
^

Por eso cart.get(0) devuelve directamente un String, sin ninguna conversión.

A la derecha, los corchetes angulares están vacíos: new ArrayList<>(). El compilador toma el tipo del lado izquierdo.

Normalmente la variable se declara con la interfaz List, y la clase ArrayList solo aparece después de new:

List<String> cart = new ArrayList<>();
cart.add("Pan");
System.out.println(cart.get(0));

Es el polimorfismo del módulo «Herencia y polimorfismo»: List dice qué puede hacer una lista, y ArrayList es una forma de hacerlo. Necesitas dos import: java.util.ArrayList y java.util.List. A partir de aquí, la lección no los muestra.

La idea

Índices: get, set, add, indexOf

Una lista tiene índices como un array, del 0 a size() - 1. Pero en lugar de [ ] usas métodos:

List<String> tasks = new ArrayList<>();
tasks.add("Factura de marzo");
tasks.add("Llamar al banco");
tasks.add("Informe del almacén");
System.out.println(tasks.get(0));
tasks.set(1, "Reunión en el banco");
tasks.add(0, "Café con el equipo");
System.out.println(tasks);
System.out.println(tasks.indexOf("Informe del almacén"));
System.out.println(tasks.contains("Llamar al banco"));
Salida
Factura de marzo
[Café con el equipo, Factura de marzo, Reunión en el banco, Informe del almacén]
3
false
  • get(i) lee, y set(i, x) reemplaza.
  • add(i, x) inserta en el índice i y desplaza el resto a la derecha.
  • indexOf(x) busca y devuelve el índice o -1, y contains(x) solo dice si está.
Más precisamente, para curiosos
  • Un índice fuera de la lista detiene el programa, como con un array:
tasks.get(3) con 3 tareas
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 3 out of bounds for length 3
  • contains e indexOf comparan con equals, no con ==. Por eso encuentran un texto igual, no solo el mismo objeto.
  • Insertar y quitar al principio es lento en una lista grande: cada elemento después del índice se mueve un lugar. Con un millón de elementos, son un millón de movimientos.

Pruébalo

¿Quién sigue?

La lista de pacientes que esperan en el médico. Juan entra, y Ana tiene cita, así que ocupa el segundo lugar.

¿Qué imprimirá el programa?

List<String> waiting = new ArrayList<>();
waiting.add("Juan");
waiting.add("María");
waiting.add("Pedro");
waiting.remove(0);
waiting.add(1, "Ana");
System.out.println(waiting + " " + waiting.size());
Respuesta

La idea

Recorrer los elementos

Recorres una lista como un array: con for-each cuando solo necesitas los elementos, o con un índice cuando también necesitas el número:

List<String> cart = new ArrayList<>();
cart.add("Pan");
cart.add("Café");
cart.add("Mantequilla");
for (String item : cart) {
System.out.println("- " + item);
}
for (int i = 0; i < cart.size(); i++) {
System.out.println((i + 1) + ". " + cart.get(i));
}
Salida
- Pan
- Café
- Mantequilla
1. Pan
2. Café
3. Mantequilla
Más precisamente, para curiosos
  • No quites elementos de una lista mientras la recorres con for-each. El programa se detiene:
for (String item : cart) {
if (item.equals("Pan")) {
cart.remove(item);
}
}
Error
Exception in thread "main" java.util.ConcurrentModificationException

La excepción no siempre llega. Si quitas el penúltimo elemento, el bucle termina antes y el último nunca se comprueba. Así que no cuentes con ella.

  • Si tienes que quitar, recorre con un índice de atrás hacia delante. Así, quitar no desplaza los elementos que todavía no has comprobado:
List<String> cart = new ArrayList<>();
cart.add("Pan");
cart.add("Leche");
cart.add("Pan");
for (int i = cart.size() - 1; i >= 0; i--) {
if (cart.get(i).equals("Pan")) {
cart.remove(i);
}
}
System.out.println(cart);
Salida
[Leche]
En el módulo «Java moderno» verás una forma más corta: removeIf.

La idea

Números en una lista: Integer

Una lista de puntuaciones se ve así, pero no compila:

ArrayList<int> scores = new ArrayList<>();
Error de compilación
Main.java:5: error: unexpected type
ArrayList<int> scores = new ArrayList<>();
^
required: reference
found: int

Los corchetes angulares necesitan una clase, y int no es una clase. Por eso cada tipo primitivo tiene una clase envoltorio: Integer para int, Double para double, Boolean para boolean, Character para char.

List<Integer> scores = new ArrayList<>();
scores.add(85);
scores.add(64);
scores.add(91);
int total = 0;
for (int score : scores) {
total += score;
}
System.out.println("Media: " + (double) total / scores.size());
Salida
Media: 80.0

scores.add(85) recibe un int, pero la lista quiere un Integer. Java lo mete en un envoltorio sola. Esto se llama autoboxing. En el bucle, int score es lo contrario: unboxing. Casi siempre no notas la diferencia.

Más precisamente, para curiosos
  • Compara los valores Integer con equals, no con ==. == comprueba si las dos variables apuntan al mismo objeto:
Integer a = 127, b = 127;
Integer c = 128, d = 128;
System.out.println(a == b);
System.out.println(c == d);
System.out.println(c.equals(d));
Salida
true
false
true

Java guarda objetos ya hechos para los números del -128 al 127, así que con 127 las dos flechas apuntan a un mismo objeto. Con 128 hay dos objetos.

  • Un Integer puede ser null, y un int no. Si desenvuelves null, el programa se detiene con NullPointerException. Te lo encontrarás en la lección sobre HashMap.

Pruébalo

Encuentra el error

Un teatro guarda los asientos ocupados en una lista. Un cliente deja el asiento 2, y el programa debería imprimir [12, 7, 5]. En cambio imprime [12, 2, 5]. ¿Qué línea tiene el error?

Elige la línea con el error.

La idea

Ordenar y una lista ya hecha

Collections.sort ordena la lista en el sitio: el texto en orden alfabético, los números por tamaño. La clase Collections también está en java.util.

List<String> cities = new ArrayList<>();
cities.add("Sevilla");
cities.add("Madrid");
cities.add("Valencia");
Collections.sort(cities);
System.out.println(cities);
List<Integer> prices = new ArrayList<>(List.of(30, 5, 12));
Collections.sort(prices);
System.out.println(prices);
Salida
[Madrid, Sevilla, Valencia]
[5, 12, 30]

List.of(30, 5, 12) crea una lista ya hecha con los valores que enumeras. Pero es inmutable: no puedes añadir, quitar ni ordenar en ella.

List<String> days = List.of("lun", "mar", "mié");
days.add("jue");
Error
Exception in thread "main" java.lang.UnsupportedOperationException

Por eso los precios de arriba se copian a una ArrayList nueva: new ArrayList<>(List.of(30, 5, 12)) es una lista que puedes cambiar.

Más precisamente, para curiosos
  • El texto se ordena por los códigos de sus caracteres. Las mayúsculas van antes que las minúsculas, así que «Pera» va antes que «manzana»:
List<String> fruit = new ArrayList<>(List.of("manzana", "Pera", "banana", "Uva"));
Collections.sort(fruit);
System.out.println(fruit);
Salida
[Pera, Uva, banana, manzana]
  • Cómo ordenar tus propios objetos, por ejemplo productos por precio, lo verás en la lección «Clases anidadas».

Ejercicio

Listas en clases

Resuelve las tareas en tu editor. La lista es un campo private de la clase, y main solo llama a sus métodos.

1. Una estantería. Una clase BookShelf con un método addBook(String title) que pone el libro al final, y takeBook(), que devuelve y quita el primer libro (o null si la estantería está vacía). Añade count() y toString(). En main, añade «Bajo el yugo», «Tabaco» y «El candelabro de hierro», toma un libro e imprime el resto.

Salida
Tomado: Bajo el yugo
2 quedan: [Tabaco, El candelabro de hierro]

2. Un registro de eventos. Una clase Event con una fecha y una descripción, y una clase EventLog con los métodos addEvent(String date, String description) y getEventsBetween(String from, String to), que devuelve una lista con los eventos entre las dos fechas, incluidas. Las fechas son texto con la forma 2026-03-31. Con esta forma, el texto se ordena igual que las fechas, así que compareTo de la lección sobre texto las compara correctamente. Añade cuatro eventos: 2026-01-15 «Inventario anual», 2026-03-31 «Informe del primer trimestre», 2026-05-20 «Nuevo proveedor de café», 2026-09-01 «Nuevo curso escolar». Imprime los del 2026-03-01 al 2026-06-30.

Salida
2026-03-31 Informe del primer trimestre
2026-05-20 Nuevo proveedor de café

3. Un cuaderno. Una clase Note con un título y un contenido, y una clase Notebook con los métodos addNote, findNote(String title) (devuelve la nota o null), deleteNote(String title) (devuelve true si borró algo) y count(). Añade las notas «POO», «Colecciones» y «SQL», busca «Colecciones», borra «POO» y comprueba que ya no está.

Salida
Colecciones: ArrayList crece sola
Borrada: POO
Notas: 2
No hay ninguna nota de POO.

4. Cálculo mental. Una clase Question con un texto y la respuesta correcta, y una clase MathGame con una lista de preguntas, un método addQuestion y un método start(Scanner in). start hace las preguntas una a una, lee la respuesta y al final dice cuántas fueron correctas. Las preguntas son 2 + 2, 3 * 4 y 15 - 9.

Entrada
4
12
7
Salida
2 + 2 = ¡Correcto!
3 * 4 = ¡Correcto!
15 - 9 = Incorrecto, la respuesta es 6.
Puntuación: 2 de 3

Más tareas

  • Sin repetidos. Lee 6 nombres y reúnelos en una lista, saltándote los que ya están. Para la entrada Juan, María, Juan, Ana, María, Pedro la salida es [Juan, María, Ana, Pedro].
  • El producto más caro. Una List<Double> guarda precios. Encuentra el más alto y su índice, sin Collections.
  • La lista al revés. Escribe un método que devuelva una lista nueva con los mismos elementos en orden inverso. No cambies el original.
  • Una lista de la compra. Lee las órdenes add milk, remove milk, list y end, y guarda la lista entre ellas. La orden de quitar un producto que no está dice «No existe ese producto.».

Test

Ponte a prueba

1. ¿Por qué una ArrayList es más práctica que un array para un carrito de compras?
2. ¿Qué dice <String> en List<String> cart?
3. List<Integer> seats contiene [12, 2, 7, 5]. ¿Qué queda después de seats.remove(2)?
4. ¿Por qué no puedes escribir ArrayList<int>?
5. List<String> days = List.of("lun", "mar"); ¿Qué pasa con days.add("mié")?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·