Cola (queue)
Una colección en la que se entra por el final y se sale por el principio: el primero que llega es el primero que sale (FIFO). Colas de impresión, de mensajes, de procesos o el recorrido en anchura.
Visualízalo paso a paso
Cambia los datos, dale a reproducir y sigue cada paso en el dibujo, en la línea de Java que se ejecuta y en sus variables.
Cola circular
Escribe las operaciones (encolar un nombre o desencolar) y mira cómo frente y fin dan la vuelta al array sin mover a nadie.
- fuera de juego
Paso 1
Un array de 5 casillas. frente señala al primero que saldrá y fin a la casilla donde entrará el siguiente; al pasar de la última, vuelven a la 0 (por eso es circular).
1class ColaCircular {
2 private final String[] a;
3 private int frente = 0, fin = 0, tam = 0;
4
5 ColaCircular(int capacidad) { a = new String[capacidad]; }
6
7 boolean encolar(String x) {
8 if (tam == a.length) return false;
9 a[fin] = x;
10 fin = (fin + 1) % a.length;
11 tam++;
12 return true;
13 }
14
15 String desencolar() {
16 if (tam == 0) return null;
17 String x = a[frente];
18 a[frente] = null;
19 frente = (frente + 1) % a.length;
20 tam--;
21 return x;
22 }
23}Variables
- frente
- 0
- fin
- 0
- tam
- 0 de 5
Atajos con el foco dentro del visualizador: ← → paso a paso, Espacio reproducir o pausar, Inicio/Fin ir al principio o al final.
La idea
Una cola funciona como la de un supermercado: los elementos entran por el final (encolar) y salen por el principio (desencolar), así que salen en el mismo orden en que llegaron. Es FIFO: first in, first out.
Implementarla sobre un array tiene una trampa. Si al desencolar se mueven todos una posición a la izquierda, cada salida cuesta O(n). Si no se mueven y solo avanza el índice del frente, el array se va quedando vacío por la izquierda mientras se llena por la derecha.
La solución es el array circular: dos índices, frente y final, que avanzan siempre hacia la derecha y, al pasar de la última casilla, vuelven a la 0 con (i + 1) % capacidad. Nadie se mueve nunca de su casilla, así que encolar y desencolar son O(1); el array solo se copia a otro más grande cuando se llena de verdad.
Las colas aparecen siempre que algo se produce más rápido de lo que se consume: documentos que esperan a la impresora, peticiones que esperan a un servidor, procesos que esperan a la CPU, mensajes entre microservicios. También son la pieza del recorrido en anchura (BFS) de árboles y grafos.
Cuándo usarlo
- Atender peticiones, tareas o mensajes en el orden en que llegan.
- Comunicar un hilo que produce datos con otro que los consume (
BlockingQueue). - Recorrer un árbol o un grafo por niveles (BFS) y calcular distancias mínimas sin pesos.
- Planificar procesos por turnos (Round Robin) o simular colas reales.
Cuándo no
- Si importa la prioridad y no el orden de llegada: una cola de prioridad (montículo).
- Si se necesita lo último que entró: una pila.
Paso a paso
- Encolar (offer). Se pone el elemento en la posición final,
a[(frente + tam) % capacidad], ytam++. Si está llena, se copia a un array mayor. - Desencolar (poll). Se devuelve
a[frente], se avanzafrente = (frente + 1) % capacidadytam--. Nadie más se mueve. - Consultar el primero (peek). Se mira
a[frente]sin sacarlo. - Crecer. Al copiar a un array mayor se copian en orden, empezando por el frente, para que la parte que había dado la vuelta quede seguida.
El código
Una cola circular genérica
La cola de una impresora: el array empieza con 4 casillas y los índices dan la vuelta; si se llenara, crecería.
1import java.util.NoSuchElementException;
2
3public class Main {
4 /** Una cola sobre un array circular: frente y final dan la vuelta al llegar al final del array. */
5 static class Cola<T> {
6 private Object[] a = new Object[4];
7 private int frente = 0, tam = 0;
8
9 void encolar(T x) {
10 if (tam == a.length) crecer();
11 a[(frente + tam) % a.length] = x; // el final es frente + tam, dando la vuelta
12 tam++;
13 }
14
15 @SuppressWarnings("unchecked")
16 T desencolar() {
17 if (tam == 0) throw new NoSuchElementException("cola vacía");
18 T x = (T) a[frente];
19 a[frente] = null;
20 frente = (frente + 1) % a.length;
21 tam--;
22 return x;
23 }
24
25 boolean vacia() { return tam == 0; }
26
27 private void crecer() { // se copia en orden a un array el doble de grande
28 Object[] b = new Object[2 * a.length];
29 for (int i = 0; i < tam; i++) b[i] = a[(frente + i) % a.length];
30 a = b;
31 frente = 0;
32 }
33 }
34
35 public static void main(String[] args) {
36 Cola<String> impresora = new Cola<>();
37 for (String doc : new String[] {"apuntes.pdf", "foto.png", "examen.docx"}) {
38 impresora.encolar(doc);
39 System.out.println("En cola: " + doc);
40 }
41 System.out.println("Imprimiendo " + impresora.desencolar());
42 impresora.encolar("factura.pdf");
43 System.out.println("En cola: factura.pdf");
44 while (!impresora.vacia()) System.out.println("Imprimiendo " + impresora.desencolar());
45 }
46}class Cola:
"""Una cola sobre un array circular: frente y final dan la vuelta al llegar al final del array."""
def __init__(self):
self._a = [None] * 4
self._frente = self._tam = 0
def encolar(self, x):
if self._tam == len(self._a):
self._crecer()
self._a[(self._frente + self._tam) % len(self._a)] = x # el final es frente + tam, dando la vuelta
self._tam += 1
def desencolar(self):
if self._tam == 0:
raise IndexError("cola vacía")
x = self._a[self._frente]
self._a[self._frente] = None
self._frente = (self._frente + 1) % len(self._a)
self._tam -= 1
return x
def vacia(self):
return self._tam == 0
def _crecer(self): # se copia en orden a un array el doble de grande
self._a = [self._a[(self._frente + i) % len(self._a)] for i in range(self._tam)] + [None] * len(self._a)
self._frente = 0
impresora = Cola()
for doc in ["apuntes.pdf", "foto.png", "examen.docx"]:
impresora.encolar(doc)
print("En cola:", doc)
print("Imprimiendo", impresora.desencolar())
impresora.encolar("factura.pdf")
print("En cola: factura.pdf")
while not impresora.vacia():
print("Imprimiendo", impresora.desencolar())/** Una cola sobre un array circular: frente y final dan la vuelta al llegar al final del array. */
class Cola {
#a = new Array(4).fill(null);
#frente = 0;
#tam = 0;
encolar(x) {
if (this.#tam === this.#a.length) this.#crecer();
this.#a[(this.#frente + this.#tam) % this.#a.length] = x; // el final es frente + tam, dando la vuelta
this.#tam++;
}
desencolar() {
if (this.#tam === 0) throw new Error("cola vacía");
const x = this.#a[this.#frente];
this.#a[this.#frente] = null;
this.#frente = (this.#frente + 1) % this.#a.length;
this.#tam--;
return x;
}
vacia() { return this.#tam === 0; }
#crecer() { // se copia en orden a un array el doble de grande
const b = new Array(2 * this.#a.length).fill(null);
for (let i = 0; i < this.#tam; i++) b[i] = this.#a[(this.#frente + i) % this.#a.length];
this.#a = b;
this.#frente = 0;
}
}
const impresora = new Cola();
for (const doc of ["apuntes.pdf", "foto.png", "examen.docx"]) {
impresora.encolar(doc);
console.log("En cola: " + doc);
}
console.log("Imprimiendo " + impresora.desencolar());
impresora.encolar("factura.pdf");
console.log("En cola: factura.pdf");
while (!impresora.vacia()) console.log("Imprimiendo " + impresora.desencolar());using System;
// Una cola sobre un array circular: frente y final dan la vuelta al llegar al final del array.
class Cola<T> {
private T?[] a = new T?[4];
private int frente = 0, tam = 0;
public void Encolar(T x) {
if (tam == a.Length) Crecer();
a[(frente + tam) % a.Length] = x; // el final es frente + tam, dando la vuelta
tam++;
}
public T Desencolar() {
if (tam == 0) throw new InvalidOperationException("cola vacía");
T x = a[frente]!;
a[frente] = default;
frente = (frente + 1) % a.Length;
tam--;
return x;
}
public bool Vacia => tam == 0;
private void Crecer() { // se copia en orden a un array el doble de grande
var b = new T?[2 * a.Length];
for (int i = 0; i < tam; i++) b[i] = a[(frente + i) % a.Length];
a = b;
frente = 0;
}
}
class Program {
static void Main() {
var impresora = new Cola<string>();
foreach (var doc in new[] { "apuntes.pdf", "foto.png", "examen.docx" }) {
impresora.Encolar(doc);
Console.WriteLine("En cola: " + doc);
}
Console.WriteLine("Imprimiendo " + impresora.Desencolar());
impresora.Encolar("factura.pdf");
Console.WriteLine("En cola: factura.pdf");
while (!impresora.Vacia) Console.WriteLine("Imprimiendo " + impresora.Desencolar());
}
}<?php
/** Una cola sobre un array circular: frente y final dan la vuelta al llegar al final del array. */
class Cola {
private array $a;
private int $frente = 0;
private int $tam = 0;
public function __construct() { $this->a = array_fill(0, 4, null); }
public function encolar(mixed $x): void {
if ($this->tam === count($this->a)) $this->crecer();
$this->a[($this->frente + $this->tam) % count($this->a)] = $x; // el final es frente + tam, dando la vuelta
$this->tam++;
}
public function desencolar(): mixed {
if ($this->tam === 0) throw new UnderflowException("cola vacía");
$x = $this->a[$this->frente];
$this->a[$this->frente] = null;
$this->frente = ($this->frente + 1) % count($this->a);
$this->tam--;
return $x;
}
public function vacia(): bool { return $this->tam === 0; }
private function crecer(): void { // se copia en orden a un array el doble de grande
$b = array_fill(0, 2 * count($this->a), null);
for ($i = 0; $i < $this->tam; $i++) $b[$i] = $this->a[($this->frente + $i) % count($this->a)];
$this->a = $b;
$this->frente = 0;
}
}
$impresora = new Cola();
foreach (["apuntes.pdf", "foto.png", "examen.docx"] as $doc) {
$impresora->encolar($doc);
echo "En cola: $doc\n";
}
echo "Imprimiendo " . $impresora->desencolar() . "\n";
$impresora->encolar("factura.pdf");
echo "En cola: factura.pdf\n";
while (!$impresora->vacia()) echo "Imprimiendo " . $impresora->desencolar() . "\n";Salida al ejecutarlo (la misma en los 5 lenguajes)
En cola: apuntes.pdf En cola: foto.png En cola: examen.docx Imprimiendo apuntes.pdf En cola: factura.pdf Imprimiendo foto.png Imprimiendo examen.docx Imprimiendo factura.pdf
Las colas de la biblioteca
Lo habitual es usar la de la biblioteca, que ya es un array circular.
1// En Java, la cola de la biblioteca es ArrayDeque (o LinkedList) a través de la interfaz Queue
2Queue<String> cola = new ArrayDeque<>();
3cola.offer("Ana"); // encolar por el final
4cola.offer("Luis");
5String primero = cola.peek(); // "Ana", sin sacarlo
6String sale = cola.poll(); // "Ana"; poll devuelve null si está vacía (remove lanzaría excepción)from collections import deque
# deque es una cola doble: append encola por el final y popleft saca por el principio, ambas O(1)
cola = deque()
cola.append("Ana")
cola.append("Luis")
primero = cola[0] # "Ana", sin sacarlo
sale = cola.popleft() # "Ana" (con la cola vacía lanzaría IndexError)
# No uses list.pop(0): desplaza toda la lista, O(n)// JavaScript no trae una cola eficiente: shift() desplaza todo el array (O(n)).
// Para colas grandes, un índice al frente evita mover nada:
const cola = [];
let frente = 0;
cola.push("Ana"); // encolar por el final
cola.push("Luis");
const primero = cola[frente]; // "Ana", sin sacarlo
const sale = cola[frente++]; // "Ana"; la cola son las posiciones frente..cola.length-1// En C#, Queue<T> (un array circular por dentro)
var cola = new Queue<string>();
cola.Enqueue("Ana"); // encolar por el final
cola.Enqueue("Luis");
string primero = cola.Peek(); // "Ana", sin sacarlo
string sale = cola.Dequeue(); // "Ana"
bool hay = cola.TryDequeue(out var otro); // sin excepción si está vacía// En PHP, SplQueue (o un array con array_shift, que es O(n))
$cola = new SplQueue();
$cola->enqueue("Ana"); // encolar por el final
$cola->enqueue("Luis");
$primero = $cola->bottom(); // "Ana", sin sacarlo
$sale = $cola->dequeue(); // "Ana"Traza: cola circular de capacidad 4
| Operación | a[0..3] | frente | final | tam |
|---|---|---|---|---|
| encolar A | A · · · | 0 | 1 | 1 |
| encolar B | A B · · | 0 | 2 | 2 |
| encolar C | A B C · | 0 | 3 | 3 |
| desencolar → A | · B C · | 1 | 3 | 2 |
| desencolar → B | · · C · | 2 | 3 | 1 |
| encolar D | · · C D | 2 | 0 | 2 |
| encolar E | E · C D | 2 | 1 | 3 |
| encolar F | E F C D | 2 | 2 | 4 |
Al encolar E y F, el final da la vuelta a las casillas 0 y 1, que se habían quedado libres: el array se reutiliza sin mover a nadie.
Complejidad
| Operación | Array circular | Array desplazando | Lista enlazada |
|---|---|---|---|
| Encolar | O(1) amortizado | O(1) | O(1) con referencia al último |
| Desencolar | O(1) | O(n) | O(1) |
| Consultar el primero | O(1) | O(1) | O(1) |
Memoria: O(n). ArrayDeque es un array circular; LinkedList también sirve como cola, pero cada nodo ocupa bastante más memoria.
- Mejor caso: O(1)
- Caso medio: O(1)
- Peor caso: O(n)
Encolar y desencolar son O(1) con un array circular; solo crecer (copiar a un array mayor) cuesta n. Las curvas grises son las demás clases, para comparar.
En la práctica
- La cola de impresión, la de envío de correos o la de tareas en segundo plano de cualquier aplicación web.
- El planificador de un sistema operativo mantiene una cola de procesos listos; Round Robin es literalmente una cola con turnos de tiempo fijo.
- Los sistemas de mensajería (RabbitMQ, Kafka, SQS) son colas entre programas que no tienen por qué estar funcionando a la vez.
BlockingQueuecomunica hilos productores y consumidores en Java sin carreras.
Errores típicos
- Desencolar moviendo todos los elementos a la izquierda (
forque desplaza): convierte cada salida en O(n). - Olvidar el
% capacidadal avanzar un índice: se sale del array. - Distinguir mal «llena» de «vacía» cuando frente == final: por eso se guarda también
tam. - En JavaScript, usar
array.shift()como desencolar en colas grandes: desplaza todo el array cada vez. - Con
Queue, usarremove()/element()en vez depoll()/peek()sin comprobar si está vacía: lanzan excepción.
Ejercicios
Cada ejercicio se corrige solo con sus pruebas (algunas ocultas). Escribe tu solución en el editor y pulsa Ejecutar o Comprobar; la solución explicada está debajo, por si te atascas.
1. Una ventanilla y sus tiempos de espera
Hay una sola ventanilla y los clientes se atienden por orden de llegada. Cada línea es un cliente: nombre, minuto de llegada y minutos que tarda su gestión (las llegadas no bajan nunca). Calcula cuánto espera cada uno, cuándo sale, la espera total y quién esperó más. El main ya lee y valida y mete los clientes en una cola: completa atender.
- Entrada: líneas como
Ana 0 5(llega en el minuto 0 y tarda 5). - Salida:
Ana: espera 0 min y sale en el minuto 5por cliente y, al final,Espera total: T min · la más larga: M min (Nombre)(si empatan, el primero). - Errores:
Línea no válida: «…»(también si llega antes que el anterior) yNo hay clientes.
Ejemplo
Ana 0 5 Luis 1 3 Eva 2 4 Pablo 20 2
Ana: espera 0 min y sale en el minuto 5 Luis: espera 4 min y sale en el minuto 8 Eva: espera 6 min y sale en el minuto 12 Pablo: espera 0 min y sale en el minuto 22 Espera total: 10 min · la más larga: 6 min (Eva)
Ver la solución explicada
1import java.util.*;
2
3public class Main {
4 record Cliente(String nombre, int llegada, int duracion) { }
5
6 /** Atiende a todos por orden de llegada con una sola ventanilla y escribe la espera de cada uno.
7 Devuelve {espera total, espera máxima, posición en la cola del que más esperó}. */
8 static int[] atender(Queue<Cliente> cola) {
9 int reloj = 0, total = 0, maxima = -1, quien = -1, k = 0;
10 while (!cola.isEmpty()) {
11 Cliente c = cola.poll();
12 int empieza = Math.max(reloj, c.llegada()); // si la ventanilla está libre, empieza al llegar
13 int espera = empieza - c.llegada();
14 reloj = empieza + c.duracion();
15 System.out.println(c.nombre() + ": espera " + espera + " min y sale en el minuto " + reloj);
16 total += espera;
17 if (espera > maxima) {
18 maxima = espera;
19 quien = k;
20 }
21 k++;
22 }
23 return new int[] {total, maxima, quien};
24 }
25
26 public static void main(String[] args) {
27 Scanner sc = new Scanner(System.in);
28 Queue<Cliente> cola = new ArrayDeque<>();
29 List<String> nombres = new ArrayList<>();
30 int ultima = 0;
31 while (sc.hasNextLine()) {
32 String linea = sc.nextLine().trim();
33 if (linea.isEmpty()) continue;
34 String[] p = linea.split("\\s+");
35 if (p.length != 3 || !p[1].matches("\\d{1,4}") || !p[2].matches("[1-9]\\d{0,2}") || Integer.parseInt(p[1]) < ultima) {
36 System.out.println("Línea no válida: «" + linea + "»");
37 continue;
38 }
39 ultima = Integer.parseInt(p[1]);
40 cola.offer(new Cliente(p[0], ultima, Integer.parseInt(p[2])));
41 nombres.add(p[0]);
42 }
43 if (cola.isEmpty()) {
44 System.out.println("No hay clientes");
45 return;
46 }
47 int[] r = atender(cola);
48 System.out.println("Espera total: " + r[0] + " min · la más larga: " + r[1] + " min (" + nombres.get(r[2]) + ")");
49 }
50}La cola garantiza el orden de llegada y el reloj resume todo lo demás: es una simulación de eventos discretos, la técnica con la que se dimensionan cajas, servidores o centralitas.
Si la ventanilla queda libre antes de que llegue el siguiente, el reloj salta a su llegada y su espera es 0.
2. Planificación Round Robin
El planificador Round Robin reparte la CPU por turnos: el primer proceso de la cola la usa como mucho q unidades de tiempo (el quantum); si no ha terminado, vuelve al final de la cola. Todos los procesos llegan en el instante 0, en el orden de la entrada. Escribe el diagrama de Gantt, cuándo termina cada proceso y la suma de esos tiempos. Completa planificar.
- Entrada: línea 1 el quantum (1 a 99); después, una línea por proceso con su nombre y su duración (1 a 99).
- Salida: el diagrama en una línea, como
P1(0-2) P2(2-4) P1(4-5); luegoP1 termina en 5por proceso (en el orden de la entrada) ySuma de los tiempos de finalización: S. - Errores:
Quantum no válido: «…»,Proceso no válido: «…»(también un nombre repetido) yNo hay procesos.
Ejemplo
2 P1 5 P2 3 P3 1
P1(0-2) P2(2-4) P3(4-5) P1(5-7) P2(7-8) P1(8-9) P1 termina en 9 P2 termina en 8 P3 termina en 5 Suma de los tiempos de finalización: 22
Ver la solución explicada
1import java.util.*;
2
3public class Main {
4 static final class Proceso {
5 final String nombre;
6 int resta;
7 Proceso(String nombre, int duracion) { this.nombre = nombre; this.resta = duracion; }
8 }
9
10 /** Round Robin: cada proceso usa la CPU como mucho q unidades y, si no ha terminado, vuelve al
11 final de la cola. Devuelve el diagrama (P1(0-2) P2(2-4) …) y apunta en fin cuándo acaba cada uno. */
12 static String planificar(Queue<Proceso> cola, int q, Map<String, Integer> fin) {
13 StringJoiner gantt = new StringJoiner(" ");
14 int t = 0;
15 while (!cola.isEmpty()) {
16 Proceso p = cola.poll();
17 int usa = Math.min(q, p.resta);
18 gantt.add(p.nombre + "(" + t + "-" + (t + usa) + ")");
19 t += usa;
20 p.resta -= usa;
21 if (p.resta > 0) cola.offer(p);
22 else fin.put(p.nombre, t);
23 }
24 return gantt.toString();
25 }
26
27 public static void main(String[] args) {
28 Scanner sc = new Scanner(System.in);
29 String primera = sc.hasNextLine() ? sc.nextLine().trim() : "";
30 if (!primera.matches("[1-9]\\d?")) {
31 System.out.println("Quantum no válido: «" + primera + "»");
32 return;
33 }
34 int q = Integer.parseInt(primera);
35 Queue<Proceso> cola = new ArrayDeque<>();
36 List<String> orden = new ArrayList<>();
37 while (sc.hasNextLine()) {
38 String linea = sc.nextLine().trim();
39 if (linea.isEmpty()) continue;
40 String[] p = linea.split("\\s+");
41 if (p.length != 2 || !p[1].matches("[1-9]\\d?") || orden.contains(p[0])) {
42 System.out.println("Proceso no válido: «" + linea + "»");
43 continue;
44 }
45 cola.offer(new Proceso(p[0], Integer.parseInt(p[1])));
46 orden.add(p[0]);
47 }
48 if (cola.isEmpty()) {
49 System.out.println("No hay procesos");
50 return;
51 }
52 Map<String, Integer> fin = new HashMap<>();
53 System.out.println(planificar(cola, q, fin));
54 int suma = 0;
55 for (String n : orden) {
56 System.out.println(n + " termina en " + fin.getOrDefault(n, 0));
57 suma += fin.getOrDefault(n, 0);
58 }
59 System.out.println("Suma de los tiempos de finalización: " + suma);
60 }
61}Round Robin es una cola en la que los elementos pueden volver a entrar: nadie espera más de (n − 1)·q unidades por turno, por eso da buen tiempo de respuesta a los procesos interactivos.
Con un quantum muy grande se comporta como FCFS (el primero que llega, hasta el final); con uno muy pequeño, los cambios de contexto se comen la CPU.
Test
Test: Cola (queue)
0/5 respondidas · 0 aciertosElige una respuesta en cada pregunta: verás al momento si es correcta y por qué. Con un 80 % de aciertos se da por superada.
1.Se encolan A, B y C y se desencola una vez. ¿Quién sale?
2.En una cola circular de capacidad 5, el final está en la posición 4. ¿Dónde se pondrá el siguiente?
3.¿Por qué no conviene desencolar desplazando todos los elementos?
4.¿Qué método de Queue devuelve null si la cola está vacía en lugar de lanzar una excepción?
5.¿Qué recorrido de un grafo usa una cola?