1. Interfaz Queue: cola clásica (FIFO)
En programación, como en la vida, hay situaciones en las que no solo es importante almacenar un conjunto de elementos, sino gestionar el orden en que se procesan. Imagina una cola en el supermercado: quien está más cerca del comienzo se atiende antes. Este es el principio FIFO — «primero en entrar — primero en salir». En cambio, una pila de platos en un armario funciona según el principio LIFO — «último en entrar — primero en salir». En Java, estos principios se reflejan en colecciones: colas Queue, colas de doble extremo Deque y pilas Stack.
Dónde se utilizan:
- Procesamiento de tareas por orden de llegada (cola de trabajos, impresión de documentos, manejo de eventos).
- Implementación de deshacer/rehacer (undo/redo) — pila.
- Análisis de expresiones, recorrido de árboles y grafos (búsqueda en anchura/profundidad), entre otros.
¿Qué es una cola?
Una cola (Queue) es una colección basada en el principio FIFO. Los elementos se añaden al final y se extraen del inicio. Como en una tienda: quien llegó antes, se atiende primero.
Métodos principales de la interfaz Queue
| Método | Qué hace | Devuelve |
|---|---|---|
|
Añade un elemento al final (sin excepción) | true/false |
|
Añade un elemento al final (lanza excepción) | true/Exception |
|
Elimina y devuelve el primer elemento | elemento/null |
|
Elimina y devuelve el primer elemento | elemento/Exception |
|
Devuelve el primer elemento sin eliminarlo | elemento/null |
|
Devuelve el primer elemento sin eliminarlo | elemento/Exception |
Seguramente has notado pares de métodos con comportamientos similares. La diferencia está en la «suavidad»: offer, poll, peek devuelven un valor especial en caso de fallo (normalmente null o false) y no lanzan excepciones, mientras que add, remove, element lanzan excepciones en situaciones límite (cola vacía, a veces desbordamiento).
Ejemplo: cola de tareas
import java.util.*;
public class QueueDemo {
public static void main(String[] args) {
Queue<String> tasks = new LinkedList<>();
tasks.offer("Cepillarse los dientes");
tasks.offer("Hacer ejercicios");
tasks.offer("Tomar café");
while (!tasks.isEmpty()) {
String task = tasks.poll(); // Tomamos la tarea del principio de la cola
System.out.println("Ejecutando: " + task);
}
}
}
Salida:
Ejecutando: Cepillarse los dientes
Ejecutando: Hacer ejercicios
Ejecutando: Tomar café
¿Por qué se usa con más frecuencia LinkedList?
Una interfaz es un contrato y hay varias implementaciones. A menudo se usa LinkedList porque añade/elimina rápidamente en ambos extremos. Sin embargo, una excelente alternativa es ArrayDeque. También hay variantes especializadas: PriorityQueue (cola por prioridades), de la que hablaremos más abajo.
2. Interfaz Deque: cola de doble extremo
Deque (Double Ended Queue, «dek») es una cola donde se pueden añadir y eliminar elementos por ambos extremos: desde el inicio y desde el final. Como un autobús con dos puertas: se puede entrar/salir por cualquiera de los lados.
Deque = soldado universal: puede funcionar como una cola normal (FIFO), como una pila (LIFO) y como un híbrido.
Métodos principales de la interfaz Deque
| Método | Qué hace | Ejemplo de uso |
|---|---|---|
|
Añadir al inicio | |
|
Añadir al final | |
|
Eliminar y devolver desde el inicio | |
|
Eliminar y devolver desde el final | |
|
Ver el inicio (sin eliminar) | |
|
Ver el final (sin eliminar) | |
|
Añadir al inicio (sin excepción) | |
|
Añadir al final (sin excepción) | |
Ejemplo: Deque como cola y como pila
import java.util.*;
public class DequeDemo {
public static void main(String[] args) {
Deque<String> deque = new ArrayDeque<>();
// Lo usamos como cola (FIFO)
deque.offerLast("A");
deque.offerLast("B");
deque.offerLast("C");
System.out.println("Cola (FIFO):");
while (!deque.isEmpty()) {
System.out.println(deque.pollFirst());
}
// Lo usamos como pila (LIFO)
deque.offerLast("1");
deque.offerLast("2");
deque.offerLast("3");
System.out.println("Pila (LIFO):");
while (!deque.isEmpty()) {
System.out.println(deque.pollLast());
}
}
}
Salida:
Cola (FIFO):
A
B
C
Pila (LIFO):
3
2
1
¿Por qué ArrayDeque?
ArrayDeque es una implementación Deque rápida y compacta basada en array. Para pila y cola suele ser más rápida y fiable que la antigua Stack, y no tiene tamaño fijo (solo limitada por la memoria).
3. Stack: pila — último en entrar, primero en salir (LIFO)
Una pila es una colección con el principio LIFO: retiramos el elemento superior, que es el último añadido. En programación, una pila es útil para el historial de acciones (undo), el recorrido de estructuras recursivas (árboles/grafos), el cálculo de expresiones, etc.
Clase Stack (y por qué no conviene usarla)
En Java existe la clase Stack, que hereda de la obsoleta Vector. Hoy no se recomienda usarla en proyectos nuevos. Es preferible Deque/ArrayDeque.
Métodos de pila (Deque)
| Método | Qué hace |
|---|---|
|
Poner un elemento en la cima de la pila |
|
Quitar y devolver el elemento superior |
|
Ver el elemento superior |
Atención: en Deque estas operaciones corresponden a los métodos addFirst/removeFirst/peekFirst, pero por compatibilidad también existen los nombres «de pila» push/pop/peek.
Ejemplo: pila con ArrayDeque
import java.util.*;
public class StackDemo {
public static void main(String[] args) {
Deque<String> stack = new ArrayDeque<>();
stack.push("Primero");
stack.push("Segundo");
stack.push("Tercero");
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
}
Salida:
Tercero
Segundo
Primero
¿Por qué no Stack?
- Stack está sincronizada (más lenta) y hereda de la obsoleta Vector.
- ArrayDeque suele ser más rápida y compacta, no bloquea hilos.
- Si ves Stack en código nuevo, normalmente es candidata a ser reemplazada por ArrayDeque.
4. Cuándo y qué usar
| Estructura | Principio | Dónde usar | Ejemplo de implementación |
|---|---|---|---|
| Queue | FIFO | Cola de tareas, procesamiento de eventos, impresión, colas de clientes | |
| Stack | LIFO | Undo/redo, recursión, analizadores, recorrido de árboles | |
| Deque | FIFO/LIFO | Buffers, palíndromos, colas bidireccionales, tareas universales | |
Ejemplos de la vida real:
- Cola en el banco — Queue.
- Historial de acciones en un editor — Stack.
- Cola de autobuses, entrada/salida por ambos lados — Deque.
5. Ejemplos de código: implementamos cola y pila en una miniaplicación
Ejemplo 1: Cola de impresión de documentos
import java.util.*;
public class PrintQueueApp {
public static void main(String[] args) {
Queue<String> printQueue = new ArrayDeque<>();
printQueue.offer("Documento1.pdf");
printQueue.offer("Documento2.docx");
printQueue.offer("Documento3.xls");
while (!printQueue.isEmpty()) {
String doc = printQueue.poll();
System.out.println("Imprimiendo: " + doc);
}
}
}
Ejemplo 2: Pila de deshacer (undo)
import java.util.*;
public class UndoStackApp {
public static void main(String[] args) {
Deque<String> undoStack = new ArrayDeque<>();
undoStack.push("Inserción de texto");
undoStack.push("Cambio de color");
undoStack.push("Eliminación de imagen");
System.out.println("Última acción: " + undoStack.peek()); // Eliminación de imagen
while (!undoStack.isEmpty()) {
System.out.println("Deshacer: " + undoStack.pop());
}
}
}
Ejemplo 3: Cola de doble extremo
import java.util.*;
public class DequeApp {
public static void main(String[] args) {
Deque<String> deque = new ArrayDeque<>();
deque.addFirst("A");
deque.addLast("B");
deque.addFirst("C");
deque.addLast("D");
System.out.println("Extraemos desde el inicio: " + deque.removeFirst()); // C
System.out.println("Extraemos desde el final: " + deque.removeLast()); // D
System.out.println("Queda: " + deque); // [A, B]
}
}
6. Particularidades de implementación y matices
- ArrayDeque es la implementación de cola/pila más rápida y universal para la mayoría de las tareas.
- LinkedList también implementa Deque; puede ser útil, pero para colas cortas suele ser más lenta que ArrayDeque.
- PriorityQueue es una cola por prioridad: el orden lo determinan las prioridades (naturales o a través de un Comparator); no es una cola FIFO normal.
- Stack es obsoleta y está sincronizada; en proyectos nuevos se prefiere ArrayDeque.
- Deque es cómoda porque permite trabajar tanto con el inicio como con el final de la estructura, cubriendo escenarios de colas y pilas con una única abstracción.
7. Errores típicos al trabajar con colas y pilas
Error n.º 1: Usar Stack en lugar de Deque.
Muchos principiantes ven la clase Stack y la eligen «por el nombre». En proyectos modernos, en su lugar se utiliza ArrayDeque (o LinkedList) con los métodos push/pop.
Error n.º 2: Violación del principio de la estructura.
Intentan acceder a elementos de una cola/pila por índice, por ejemplo queue.get(0) o stack.get(0). No se debe — usa los métodos correspondientes de colas y pilas (peek, poll, pop, etc.).
Error n.º 3: Uso de remove()/element()/add() sin comprobación.
Los métodos remove, element, add pueden lanzar una excepción si la estructura está vacía/saturada. Es más seguro usar los «suaves» offer, poll, peek, que devuelven valores especiales.
Error n.º 4: Uso de ArrayDeque con elementos null.
ArrayDeque no admite null. Intentar añadir null provocará NullPointerException.
Error n.º 5: Usar PriorityQueue como una cola normal.
En PriorityQueue el orden lo determinan las prioridades (naturales o definidas mediante un Comparator). Para un FIFO estricto, usa ArrayDeque o LinkedList.
Error n.º 6: Modificar la cola/pila durante un recorrido con for-each.
Eliminar elementos de una colección durante un for-each puede provocar ConcurrentModificationException. Para eliminar, usa un iterador o los métodos poll/pop en un bucle, como se muestra en los ejemplos.
GO TO FULL VERSION