Pilas en Java

En este ejemplo completo te enseñaré cómo construir una estructura de datos pila en Java utilizando nodos enlazados entre sí. Es un ejemplo de implementación completo.

Cuando pensamos en cómo funciona el botón atrás de un navegador web, estamos ante un claro ejemplo de una estructura de datos llamada pila. En esencia, cada vez que navegamos a una nueva página, guardamos la URL actual en una pila, y al pulsar atrás, simplemente recuperamos la última URL almacenada para volver a la página anterior. Vamos a construir esta pila en Java, utilizando programación orientada a objetos, para entender mejor su funcionamiento y cómo implementarla de forma eficiente.

Para empezar, definimos una interfaz llamada Pila que establezca las operaciones básicas que nuestra pila debe soportar. Estas operaciones incluyen apilar una URL, desapilar la última URL insertada y obtener la URL que está en la cima sin eliminarla. Además, es útil contar con métodos que nos indiquen la longitud de la pila y si esta está vacía.

public interface Pila {
    void apilar(URL url);
    URL desapilar();
    URL obtener();
    int longitud();
    boolean vacia();
}

Con la interfaz definida, pasamos a la implementación concreta. Elegimos una lista enlazada para representar la pila, donde cada nodo contiene una URL y una referencia al siguiente nodo. Creamos una clase interna Nodo que almacena estos datos.

public class MiPila implements Pila {
    private class Nodo {
        URL url;
        Nodo siguiente;

        Nodo(URL url) {
            this.url = url;
            this.siguiente = null;
        }
    }

    private Nodo cima = null;
    private int longitud = 0;

    // Métodos a implementar...
}

La variable cima apunta al nodo que está en la parte superior de la pila, y longitud mantiene el conteo de elementos para facilitar operaciones como obtener la longitud o verificar si la pila está vacía sin necesidad de recorrer toda la estructura.

Para apilar una URL, creamos un nuevo nodo con la URL proporcionada y lo insertamos al principio de la lista enlazada. Esto se logra haciendo que el nuevo nodo apunte al nodo que actualmente es la cima, y luego actualizando la cima para que sea el nuevo nodo. Incrementamos la longitud para mantener el conteo actualizado.

@Override
public void apilar(URL url) {
    Nodo nuevoNodo = new Nodo(url);
    nuevoNodo.siguiente = cima;
    cima = nuevoNodo;
    longitud++;
}

Obtener la URL en la cima es sencillo: si la pila está vacía (cima es null), devolvemos null; de lo contrario, retornamos la URL almacenada en el nodo cima.

@Override
public URL obtener() {
    if (cima == null) {
        return null;
    }
    return cima.url;
}

Para desapilar, primero verificamos que la pila no esté vacía. Si tiene elementos, almacenamos temporalmente el nodo cima, actualizamos la cima para que apunte al siguiente nodo, desconectamos el nodo eliminado para evitar referencias colgantes y decrementamos la longitud. Finalmente, devolvemos la URL del nodo desapilado.

@Override
public URL desapilar() {
    if (cima == null) {
        return null;
    }
    Nodo nodoEliminar = cima;
    cima = cima.siguiente;
    nodoEliminar.siguiente = null; // Evitar referencias colgantes
    longitud--;
    return nodoEliminar.url;
}

Las operaciones para obtener la longitud y verificar si la pila está vacía son directas gracias al campo longitud.

@Override
public int longitud() {
    return longitud;
}

@Override
public boolean vacia() {
    return longitud == 0;
}

Para ayudarnos a visualizar el estado de la pila durante las pruebas, podemos implementar un método estático que imprima la longitud actual y la URL en la cima, o indique que la pila está vacía si no hay elementos.

public static void imprimirEstado(MiPila pila) {
    if (pila.vacia()) {
        System.out.println("La pila está vacía. Longitud: 0");
    } else {
        System.out.println("Longitud: " + pila.longitud());
        System.out.println("Cima: " + pila.obtener());
    }
}

Probamos nuestra pila creando algunas URLs y apilándolas, observando cómo cambia el estado. Por ejemplo, apilamos primero la URL de Google, luego la de Makigas, y después desapilamos mientras la pila no esté vacía, comprobando que la cima cambia correctamente y que la longitud se actualiza.

public static void main(String[] args) {
    MiPila pila = new MiPila();

    imprimirEstado(pila); // Pila vacía

    pila.apilar(new URL("https://www.google.com"));
    imprimirEstado(pila); // Longitud 1, cima Google

    pila.apilar(new URL("https://www.makigas.com"));
    imprimirEstado(pila); // Longitud 2, cima Makigas

    while (!pila.vacia()) {
        pila.desapilar();
        imprimirEstado(pila);
    }
}

Este ejemplo sencillo nos muestra cómo funciona una pila y cómo podemos implementarla para simular un historial web. Por supuesto, podemos extender esta estructura para incluir funcionalidades adicionales, como eliminar todos los elementos de golpe o acceder a posiciones específicas dentro de la pila, aunque esto último se aleja un poco de la definición clásica de pila. Sin embargo, para el caso del botón atrás de un navegador, esta implementación es suficiente y eficiente.

Lista de reproducción
  1. 1
    Qué son las estructuras de datos
    3 minutos
  2. 2
    Listas enlazadas: Teoría
    12 minutos
  3. 3
    Listas enlazadas en C
    18 minutos
  4. 4
    Listas enlazadas en Java
    17 minutos
  5. 5
    Colas: teoría
    6 minutos
  6. 6
    Colas en C
    12 minutos
  7. 7
    Colas en Java
    10 minutos
  8. 8
    Pilas: teoría
    7 minutos
  9. 9
    Pilas en C
    11 minutos
  10. 10
    Pilas en Java
    10 minutos
  11. 11
    Introducción a los árboles
    11 minutos
  12. 12
    Árboles binarios
    9 minutos
  13. 13
    Árboles binarios de búsqueda (parte 1)
    10 minutos
  14. 14
    Árboles de búsqueda binaria (parte 2)
    8 minutos
  15. 15
    BSTs en C (parte 1)
    13 minutos
  16. 16
    BSTs en C (parte 2)
    8 minutos
  17. 17
    BSTs en C (y parte 3)
    18 minutos
  18. 18
    BSTs en Java (parte 1)
    9 minutos
  19. 19
    BSTs en Java (parte 2)
    11 minutos
  20. 20
    BSTs en Java (parte 3)
    10 minutos
  21. 21
    Árbol desde preorden e inorden
    8 minutos
  22. 22
    Árbol desde inorden y postorden
    8 minutos
  23. 23
    Árboles k-arios
    6 minutos
  24. 24
    Árboles B (parte 1)
    11 minutos
  25. 25
    Árboles B (parte 2)
    20 minutos
  26. 26
    Árboles B (parte 3)
    39 minutos