← Volver a Unidad 1
Profe MacónPROGRAMACIÓN AVANZADA
2.º BT TI · DGETP/UTU · 2026
CET1 · CET2
UNIDAD 01 · INTRODUCCIÓN A LA POO · LECCIÓN 04

Colecciones en Java y polimorfismo

¿Cómo guardar muchos productos y detalles de venta? Aprende a programar mediante la interfaz List, elegir implementaciones y reconocer cuándo utilizar conjuntos, mapas y estructuras ordenadas.

ListArrayListLinkedListsortSet / MapPolimorfismo
01 · Recuperamos La Viñeta

Una venta puede contener muchos detalles

Hasta ahora usamos referencias entre objetos. Pero una Venta puede contener muchos DetalleVenta, y un catálogo puede contener cientos de productos. Necesitamos una estructura que guarde una cantidad variable de referencias sin crear veinte atributos.

Diagrama UML de diseño de La Viñeta, con Venta, Producto y DetalleVenta
En nuestro modelo aparece DetalleVenta. Una venta necesita manejar varios detalles: esa es la necesidad que motiva el uso de colecciones.

Sin colección

detalle1, detalle2, detalle3… El diseño queda limitado a una cantidad conocida.

Con colección

Un atributo reúne tantos detalles como necesite la venta, sin duplicar campos ni métodos.

Idea principal: una colección guarda referencias a objetos. El modelo de dominio sigue siendo Venta, Producto y DetalleVenta; la colección resuelve cómo organizar grupos de esos objetos.
02 · Una interfaz, varias implementaciones

¿Qué significa List<E>?

List es una interfaz de Java: establece un contrato para secuencias ordenadas de elementos. Permite elementos repetidos y acceso mediante una posición que comienza en cero.

Venta.java — declaración de un atributo
import java.util.List;
import java.util.ArrayList;

public class Venta {
    private List<DetalleVenta> detalles;

    public Venta() {
        detalles = new ArrayList<>();
    }
}
List<DetalleVenta>Contrato: agregar, obtener, recorrer, eliminar…
ArrayList<DetalleVenta>Implementación basada en arreglo redimensionable
LinkedList<DetalleVenta>Implementación enlazada

La expresión List<DetalleVenta> utiliza un genérico. La letra E representa el tipo de elemento; al escribir DetalleVenta, Java comprueba que los elementos añadidos tengan el tipo esperado.

Ojo: List no es una clase que podamos construir directamente con new List<>(). Debemos crear una implementación concreta, como ArrayList.
03 · Operaciones que usaremos

Trabajar con List en una venta

Antes de elegir una implementación conviene dominar el contrato común. En este ejemplo suponemos que DetalleVenta ya tiene constructor y método calcularSubtotal(), como en las lecciones previas.

Venta.java — fragmento de métodos
private List<DetalleVenta> detalles = new ArrayList<>();

public void agregarDetalle(DetalleVenta detalle) {
    detalles.add(detalle);
}

public int cantidadDeRenglones() {
    return detalles.size();
}

public double calcularTotal() {
    double total = 0;
    for (DetalleVenta detalle : detalles) {
        total += detalle.calcularSubtotal();
    }
    return total;
}
Operación¿Para qué sirve?Ejemplo
add(e)Agrega un elementoNuevo detalle de venta
get(i)Obtiene el elemento en la posición iPrimer detalle: índice 0
size()Devuelve la cantidad de elementosNúmero de renglones
remove(i)Elimina por índiceQuitar una fila
contains(e)Busca según igualdad (equals)Consultar si hay un objeto
clear()Vacía la listaReiniciar una lista temporal
Dos cuidados: los índices van de 0 a size()-1; además, contains utiliza equals, por lo que para comparar productos por código debemos definir la igualdad correctamente o realizar una búsqueda por código.
04 · Elegimos la implementación

ArrayList frente a LinkedList

Las dos clases implementan List, pero organizan internamente sus datos de modo distinto.

ArrayList

Nuestra opción habitual

Guarda referencias en un arreglo redimensionable. Es muy eficiente para recorrer y consultar por índice.

LinkedList

Guarda elementos enlazados. Puede insertar o eliminar eficientemente cuando ya tenemos la posición mediante un iterador, pero acceder por índice requiere recorrer enlaces.

OperaciónArrayListLinkedList
get(i)O(1)O(n)
Agregar al finalO(1) amortizadoO(1)
Insertar/eliminar en medio por índiceO(n), desplaza elementosO(n) para llegar al lugar
Insertar/eliminar con iterador ya situadoPuede desplazar elementosO(1) para modificar enlaces
Recorrer secuencialmenteBuena localidad de memoriaMás objetos y enlaces

Para La Viñeta escogeremos normalmente ArrayList: vamos a listar productos, mostrarlos en JTable, recorrerlos y consultar elementos. LinkedList es importante conocerla, pero no es automáticamente más rápida al insertar o eliminar.

Regla de diseño: elegir ArrayList por defecto cuando no existe un requisito concreto que favorezca otra implementación. Medir antes de optimizar.
05 · Aprovechamos interfaces

Polimorfismo: cambia el objeto, no el contrato

La variable puede declararse con el tipo de la interfaz mientras que el objeto pertenece a una clase que la implementa. Esa es una forma de polimorfismo por subtipo.

Dos implementaciones, el mismo tipo declarado
List<Producto> productos = new ArrayList<>();

// También es válido:
productos = new LinkedList<>();

productos.add(new Producto(101, "Mafalda 1", 450, 8));
System.out.println(productos.size());

Las operaciones add y size siguen funcionando porque pertenecen al contrato List. La implementación concreta determina cómo se ejecutan internamente.

Una operación que recibe la interfaz
public static void mostrarProductos(List<Producto> productos) {
    for (Producto producto : productos) {
        System.out.println(producto.getNombre());
    }
}

Este método acepta indistintamente un ArrayList<Producto> o un LinkedList<Producto> sin duplicar el código.

Conexión con herencia: el polimorfismo también funciona cuando una variable de superclase guarda un objeto de una subclase. Más adelante lo aprovecharemos nuevamente con la interfaz CrudDAO<Producto> y su implementación JDBC.
06 · Organizar la información

Ordenar con sort y Comparator

Para mostrar un catálogo ordenado por nombre o precio podemos utilizar sort. Hay dos formas útiles: lista.sort(comparador) y Collections.sort(lista, comparador).

Ordenar productos por precio de menor a mayor
import java.util.Comparator;
import java.util.List;

productos.sort(
    Comparator.comparingDouble(Producto::getPrecio)
);
Ordenar por nombre, ignorando mayúsculas
productos.sort(
    Comparator.comparing(
        Producto::getNombre,
        String.CASE_INSENSITIVE_ORDER
    )
);
Qué está pasando: Comparator indica cómo comparar dos productos. sort modifica el orden de los elementos de la lista. No cambia el código ni el precio de cada producto.

Si quisiéramos definir el orden «natural» de la clase usaríamos Comparable, pero por ahora es más flexible pasar distintos Comparator según lo que el usuario quiere ver.

07 · Más herramientas de Java

Conjuntos, mapas y árboles: ¿qué más existe?

No todo grupo de datos necesita ser una lista. Java ofrece otras interfaces e implementaciones dentro del Java Collections Framework, cada una con un propósito.

Set — conjuntos

Evitan elementos duplicados según la igualdad definida.

HashSet: sin orden garantizado.
LinkedHashSet: conserva orden de inserción.
TreeSet: mantiene ordenados los elementos.

Ejemplo: códigos de categorías que no deben repetirse.

Map — diccionarios

Relacionan claves con valores; las claves no se repiten.

HashMap: búsqueda por clave.
LinkedHashMap: orden de inserción.
TreeMap: claves ordenadas.

Ejemplo: código de producto → producto.

Dos ejemplos para reconocer estas estructuras
Set<String> categorias = new HashSet<>();
categorias.add("Manga");
categorias.add("Manga"); // No agrega un duplicado

Map<Long, Producto> porCodigo = new HashMap<>();
porCodigo.put(101L, producto);
Producto encontrado = porCodigo.get(101L);

¿Y los árboles?

Un árbol es una estructura jerárquica de nodos. No es una única «clase colección» para cualquier árbol: Java ofrece, entre otras, TreeSet y TreeMap, que mantienen elementos o claves ordenados mediante estructuras arbóreas equilibradas.

También existen Queue (colas), Deque (doble extremo), ArrayDeque y PriorityQueue. Basta reconocerlas por ahora; las trabajaremos cuando exista un problema que las necesite.

Precisión: Map es parte del framework de colecciones, pero no extiende la interfaz Collection. Un TreeSet no es lo mismo que una lista ordenada: no conserva duplicados según su criterio de comparación.
08 · Elegimos para La Viñeta

Una guía de elección

NecesidadAbstracciónImplementación típica
Detalles de una venta, ordenados por posiciónList<DetalleVenta>ArrayList
Etiquetas sin repetidosSet<String>HashSet
Encontrar productos por códigoMap<Long, Producto>HashMap
Productos ordenados por nombreList<Producto> + ComparatorArrayList + sort
Claves siempre ordenadasNavigableMapTreeMap

Una estructura no sustituye el análisis OO: primero entendemos las reglas de la tienda y después escogemos cómo almacenar y consultar sus objetos.

Documentación mínima: indica qué colección utilizarías para los detalles de Venta, por qué y qué operación necesitas realizar con más frecuencia. Registra esa decisión junto al UML.
09 · Comprobación

¿Qué colección elegirías?

1. Un método debe aceptar listas de productos sin importar la implementación concreta. ¿Qué tipo declaramos como parámetro?

2. Necesitamos obtener productos muchas veces mediante su posición. ¿Qué implementación de List suele ser la opción más apropiada?

3. Queremos asociar cada código único con su Producto. ¿Qué estructura corresponde?

4. ¿Qué significa el polimorfismo de List?

Microentrega: en cuatro oraciones, justifica por qué una venta utilizaría List<DetalleVenta>, qué implementación seleccionarías, qué ocurre al cambiarla y cuándo necesitarías un Map en la tienda.

Continuidad: con estas herramientas podremos gestionar objetos en memoria, crear interfaces gráficas con Swing y, más adelante, implementar persistencia mediante DAO y JDBC.

Material en curso