classDiagram
class Animal {
+makeSound() : void
}
class Dog {
+makeSound() : void
}
Animal <|-- Dog
Aprender a implementar una cola (Queue) desde cero usando un arreglo (array), sin usar colecciones predefinidas de Java.
Una cola (Queue) es una estructura de datos FIFO:
➡️ First In, First Out
El primero en entrar es el primero en salir 🧺
@Override en Java?@Override es una anotación que le dice al compilador:
“Este método ya existe en la clase padre,
y quiero redefinirlo aquí con mi propia versión.”
🐶 La clase Dog hereda el método makeSound() de Animal, pero lo redefine para adaptarlo a su propio comportamiento.
@Override?🚨 El compilador no detectaría que el método está mal escrito y simplemente crearía uno nuevo.
🧡 Con @Override, recibirías un error que te advierte:
“Method does not override or implement a method from a supertype”
classDiagram
class Animal {
+makeSound() : void
}
class Dog {
+makeSound() : void
}
Animal <|-- Dog
📚 Dog extiende Animal y sobrescribe (override) el método makeSound() heredado.
toString()Por defecto, todos los objetos en Java tienen este método:
Pero podemos redefinirlo con @Override para mostrar algo más útil:
toString()💬 Así, cuando imprimimos la cola, vemos su contenido real:
[10, 20, 30, 0, 0]
| Aspecto | Descripción |
|---|---|
| 💬 Qué es | Anotación para sobrescribir métodos heredados |
| 🧱 Dónde se usa | En clases hijas que redefinen métodos (toString, equals, hashCode, etc.) |
| 🔍 Para qué sirve | Le dice al compilador que el método ya existe y debe verificarse |
| ⚙️ Beneficio | Evita errores, mejora la legibilidad y documenta la intención |
1️⃣ Usando un Array
2️⃣ Usando una Lista Enlazada
3️⃣ Usando Dos Pilas (Stacks)
En este ejercicio implementaremos la primera: Array Queue 📦
Vamos a crear una clase llamada ArrayQueue.
Esta cola debe tener los siguientes métodos:
| Método | Descripción |
|---|---|
enqueue() |
Agregar un elemento al final |
dequeue() |
Retirar el elemento del frente |
peek() |
Ver el frente sin quitarlo |
isEmpty() |
Verificar si la cola está vacía |
isFull() |
Verificar si la cola está llena |
Imagina un arreglo de tamaño 5:
[ , , , , ]
Al inicio:
frente = 0
rear = -1
🧠 Los elementos se agregan al final,
y se eliminan del frente — ¡sin mover físicamente los datos!
flowchart LR
subgraph Cola["🧮 Cola en Array"]
A0["Índice 0"] --> A1["Índice 1"] --> A2["Índice 2"] --> A3["Índice 3"] --> A4["Índice 4"]
end
style Cola fill:#fefefe,stroke:#aaa,stroke-width:2px
F([frente=0]) -.-> A0
R([rear=-1]) -.-> A4
➡️ Cuando hacemos enqueue(10), movemos rear y guardamos el valor:
Cuando retiramos (dequeue()):
[10, 20, 30, , ] frente=1cola=2
🔹 El valor 10 sigue en memoria, pero ya no forma parte de la cola lógica.
Si hacemos varios dequeue(), el puntero frente seguirá avanzando…
[10, 20, 30, , ]
frente=3 cola=4
😱 ¡A pesar de tener espacios vacíos, el arreglo parece lleno!
🧩 Solución: usar índices circulares (modulares).
Cuando rear llega al final, puede volver al inicio:
De esta forma, reutilizamos los espacios liberados. ♻️
public class ArrayQueue {
private int[] items;
private int frente;
private intcola;
private int count;
public ArrayQueue(int capacity) {
items = new int[capacity];
frente = 0;
cola = -1;
count = 0;
}
public void enqueue(int item) {
if (isFull())
throw new IllegalStateException("Cola llena");
cola = (rear + 1) % items.length;
items[rear] = item;
count++;
}
public int dequeue() {
if (isEmpty())
throw new IllegalStateException("Cola vacía");
int value = items[frente];
frente = (frente + 1) % items.length;
count--;
return value;
}
public int peek() {
if (isEmpty())
throw new IllegalStateException("Cola vacía");
return items[frente];
}
public boolean isEmpty() { return count == 0; }
public boolean isFull() { return count == items.length; }
@Override
public String toString() {
return java.util.Arrays.toString(items);
}
}flowchart LR
subgraph Circular["🔄 Cola Circular (capacidad = 5)"]
A["20"] --> B["30"] --> C["40"] --> D[" "] --> E["10"]
end
F([frente=4]) -.-> E
R([rear=2]) -.-> C
👁🗨️
frente apunta al inicio lógicorear apunta al último elemento insertadopublic static void main(String[] args) {
ArrayQueue queue = new ArrayQueue(5);
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
System.out.println(queue); // [10, 20, 30, 0, 0]
queue.dequeue();
queue.dequeue();
System.out.println(queue); // [10, 20, 30, 0, 0] (pero lógicamente: [30])
queue.enqueue(40);
queue.enqueue(50);
System.out.println(queue); // circularmente llena
}| Estructura | Significado | Orden de salida | Ejemplo |
|---|---|---|---|
| 🧺 Queue | First In, First Out | Sale el primero que entró | Fila del banco 🏦 |
| 📚 Stack | Last In, First Out | Sale el último que entró | Pila de platos 🍽️ |
flowchart LR
subgraph FIFO["🧺 Queue (FIFO)"]
A1["Entra 10"] --> A2["Entra 20"] --> A3["Entra 30"]
A3 -->|Sale primero 10| A4["[20, 30]"]
end
subgraph LIFO["📚 Stack (LIFO)"]
B1["Apila 10"] --> B2["Apila 20"] --> B3["Apila 30"]
B3 -->|Sale primero 30| B4["[20, 10]"]
end
✨ Con esta implementación:
Usamos un array circular para manejar eficientemente el espacio.
Controlamos la cola con dos punteros: frente y rear.
Practicamos conceptos de:
( % )💡 Ideal para entrevistas y evaluaciones de estructuras de datos.
🎬 ¡Nuestra ArrayQueue ya está lista para la acción! 🚀