COMPARTE ESTE ARTÍCULO

¡Hola a todos! En el artículo de hoy vamos a enfrentarnos a uno de los clásicos en los exámenes de programación en Java: la creación de estructuras de datos desde cero.

Es muy cómodo usar un ArrayList o la clase Stack que ya nos proporciona Java, pero para ser verdaderos profesionales (y para aprobar esos exámenes exigentes), necesitamos entender qué ocurre “bajo el capó”. Hoy vamos a implementar una Pila (Stack) genérica basada en arrays, gestionando la memoria manualmente y aplicando el principio LIFO (Last In, First Out).

Coge un café, abre tu IDE favorito y vamos a resolver este reto paso a paso.

Paso 1: Definir el “Contrato” con una Interfaz Genérica

En Java, las buenas prácticas nos dicen que debemos separar el comportamiento (lo que hace) de la implementación (cómo lo hace). Para ello, crearemos una interfaz genérica <E>.

La letra E significa Element y nos permitirá usar nuestra pila con cualquier tipo de objeto (Strings, Integers, objetos propios…).

public interface Pila<E> {
    void apilar(E elemento);
    E desapilar();
    boolean estaVacia();
}

Paso 2: La Clase Contenedor y el Reto del Constructor

Ahora creamos la clase Contenedor que implementará nuestra interfaz. Aquí nos encontramos con el primer obstáculo típico de examen: Java no permite instanciar arrays genéricos directamente (no puedes hacer new E[0]).

¿La solución? Instanciar un array de Object y hacer un casting explícito.

public class Contenedor<E> implements Pila<E> {
    
    // Nuestro array principal para almacenar los datos
    private E[] datos;

    // Constructor
    @SuppressWarnings("unchecked")
    public Contenedor() {
        // Inicializamos la pila vacía (tamaño 0)
        datos = (E[]) new Object[0];
    }
    
    @Override
    public boolean estaVacia() {
        return datos.length == 0;
    }
    
    // ... dejaremos apilar y desapilar para el siguiente paso
}

Paso 3: El “Truco” Mágico para Arrays Fijos (El método append)

Como bien sabemos, los arrays en Java tienen un tamaño fijo. Si inicializamos el array con tamaño 0, ¿cómo metemos datos? Necesitamos redimensionarlo dinámicamente.

Para ello, crearemos un método de apoyo usando la clase java.util.Arrays. Este método creará una copia del array original, pero con un espacio extra al final.

    // Método auxiliar para añadir un elemento
    private E[] append(E elemento, E[] arrayOriginal) {
        // Copiamos el array sumándole 1 a su longitud
        E[] nuevoArray = java.util.Arrays.copyOf(arrayOriginal, arrayOriginal.length + 1);
        // Insertamos el nuevo elemento en la última posición
        nuevoArray[arrayOriginal.length] = elemento;
        return nuevoArray;
    }

Paso 4: Implementando Apilar y Desapilar (LIFO)

Ahora que podemos redimensionar, implementar la lógica de la pila es pan comido.

Al apilar, simplemente reasignamos nuestro array datos con el resultado de nuestro método append.

Al desapilar, debemos extraer el último elemento y, muy importante, encoger el array para liberar esa referencia de memoria.

    @Override
    public void apilar(E elemento) {
        datos = append(elemento, datos);
    }

    @Override
    public E desapilar() {
        if (estaVacia()) {
            return null; // Controlamos el error si la pila está vacía
        }

        // Rescatamos el último elemento (LIFO)
        E ultimoElemento = datos[datos.length - 1];
        
        // Reducimos el tamaño del array en 1 para eliminarlo definitivamente
        datos = java.util.Arrays.copyOf(datos, datos.length - 1);
        
        return ultimoElemento;
    }

Paso 5: Ampliación Pro – Concatenar Arrays de Forma Eficiente

Muchos exámenes piden manipular arrays a bajo nivel para evaluar tu comprensión sobre rendimiento. Si te piden concatenar dos arrays, no uses un bucle for llamando a append continuamente (es muy ineficiente).

Utiliza System.arraycopy(), que es un método nativo rapidísimo:

    public static <T> T[] concatenar(T[] a, T[] b) {
        // Creamos un array con la suma de las dos longitudes
        T[] resultado = java.util.Arrays.copyOf(a, a.length + b.length);
        
        // Copiamos el array 'b' a partir de donde termina 'a'
        System.arraycopy(b, 0, resultado, a.length, b.length);
        
        return resultado;
    }

Paso 6: Poniéndolo a Prueba (El Programa Principal)

Por último, vamos a simular el uso de esta clase en el main. Recuerda un error de novato crítico: No se pueden usar tipos primitivos (como int) con genéricos. Debes usar sus clases envoltorio (Wrapper), en este caso, Integer.

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // Usamos Integer en lugar de int
        Contenedor<Integer> miPila = new Contenedor<>(); 

        System.out.println("--- GESTOR DE PILA LIFO ---");
        System.out.println("Introduce números enteros positivos (teclea -1 para terminar):");

        int numero;
        do {
            numero = sc.nextInt();
            if (numero != -1 && numero >= 0) {
                miPila.apilar(numero);
                System.out.println("Apilado: " + numero);
            }
        } while (numero != -1);

        System.out.println("\n--- VACIANDO LA PILA (Desapilando) ---");
        
        // Vaciamos la pila hasta que no quede nada
        while (!miPila.estaVacia()) {
            System.out.println("Extrayendo: " + miPila.desapilar());
        }
        
        System.out.println("La pila está vacía. ¡Programa finalizado!");
        sc.close();
    }
}

El código final sería:
interface Pila<E> {
    void apilar(E elemento);
    E desapilar();
    boolean estaVacia();
}
import java.util.Arrays;

class Contenedor<E> implements Pila<E> {

    private E[] datos;

    public Contenedor() {
        datos = (E[]) new Object[0];
    }

    @Override
    public void apilar(E elemento) {
        datos = append(elemento, datos);
    }

    @Override
    public E desapilar() {
        if (estaVacia()) {
            return null;
        }

        E ultimo = datos[datos.length - 1];
        datos = Arrays.copyOf(datos, datos.length - 1);
        return ultimo;
    }

    @Override
    public boolean estaVacia() {
        return datos.length == 0;
    }

    // Método genérico append
    public static <E> E[] append(E elemento, E[] array) {
        E[] nuevo = Arrays.copyOf(array, array.length + 1);
        nuevo[array.length] = elemento;
        return nuevo;
    }
}
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        Contenedor<Integer> pila = new Contenedor<>();

        int num;

        System.out.println("Introduce números positivos (-1 para terminar):");

        do {
            num = sc.nextInt();

            if (num != -1 && num > 0) {
                pila.apilar(num);
            }

        } while (num != -1);

        System.out.println("Desapilando elementos:");

        while (!pila.estaVacia()) {
            System.out.println(pila.desapilar());
        }

        sc.close();
    }
}



Conclusión y Errores a Evitar en el Examen

Conclusión y Errores a Evitar en el Examen

Si pruebas el código, verás que si introduces 10, 20, 30, el programa devolverá 30, 20, 10. ¡Objetivo cumplido!

Antes de tu examen, memoriza esto:

  1. Siempre comprueba si la colección está vacía antes de intentar extraer (evitarás el temido ArrayIndexOutOfBoundsException).
  2. Recuerda que al usar genéricos, Contenedor<int> dará error de compilación. Usa Contenedor<Integer>.
  3. Al manipular arrays manualmente, la clave está en reasignar la variable al nuevo array modificado: datos = Arrays.copyOf(...).

¡Aplica estos pasos y ninguna estructura de datos se te resistirá! Sigue practicando y nos vemos en la próxima clase.

Contenido restringido

Acceso de usuarios existentes
   
Registro de un nuevo usuario
*Campo necesario

Categories:

Tags:

Comments are closed

Estado de acceso
ESTADO DE ACCESO
TRADUCTORES
COMPARTENOS
Insert math as
Block
Inline
Additional settings
Formula color
Text color
#333333
Type math using LaTeX
Preview
\({}\)
Nothing to preview
Insert
error: CONTENIDO PROTEGIDO