public class ArrayStack {
    /**
     * Hier werden die Stack-Einträge gespeichert.
     */
    private int[] data;

    /**
     * Das nächste zu schreibende Element (= Anzahl der Elemente auf dem Stack)
     */
    private int topOfStack;

    /**
     * Lege einen leeren Stack an.
     */
    public ArrayStack() {
        this(5);
    }

    /**
     * Legt einen Stack mit der Anfangskapazität <code>initialCapacity</code>
     * an. Die Kapazität wirkt sich nicht auf das beobachtbare Verhalten aus
     * (sprich: sie wird bei bedarf erweitert).
     */
    public ArrayStack(int initialCapacity) {
        assert initialCapacity >= 0;
        this.data = new int[initialCapacity];
        topOfStack = 0;
    }

    /**
     * Lege einen neuen Stack an, belege ihn mit den Elementen
     * <code>elements</code>.
     */
    public ArrayStack(int[] elements) {
        this(elements.length);
        // Nicht schnell, aber dafür einfach:
        for (int elem : elements) {
            push(elem);
        }
    }

    /**
     * Copy constructor: Erzeuge eine neue Instanz als echte Kopie von
     * <code>other</code>.
     */
    public ArrayStack(ArrayStack other) {
        this.data = new int[other.data.length];
        System.arraycopy(other.data, 0, this.data, 0, other.data.length);
        this.topOfStack = other.topOfStack;
    }

    /**
     * Stelle einen neuen Wert auf den Stack, erweitere gegebenenfalls die
     * Kapazität.
     */
    public void push(int value) {
        if (this.topOfStack >= data.length) {
            extendCapacity();
        }
        this.data[this.topOfStack] = value;
        this.topOfStack++;
    }

    /**
     * Hilfsfunktion: Verdopple Kapazität.
     */
    private void extendCapacity() {
        int newLength = this.data.length * 2;
        if (newLength == 0) {
            // Zur Sicherheit: Möglicherweise war die Anfangskapazität = 0
            newLength = 2;
        }
        int[] newData = new int[newLength];
        System.arraycopy(this.data, 0, newData, 0, this.data.length);
        this.data = newData;
    }

    /**
     * Entferne den obersten Wert vom Stack und gib ihn zurück.
     */
    public int pop() {
        assert this.topOfStack > 0;
        this.topOfStack--;
        return this.data[topOfStack];
    }

    /**
     * Lies den obersten Wert vom Stack aus.
     */
    public int peek() {
        assert this.topOfStack > 0;
        return this.data[topOfStack - 1];
    }

    /**
     * Gib den momentanen Stackzustand als String zurück.
     */
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("ArrayStack[");
        for (int i = 0; i < this.topOfStack; i++) {
            if (i > 0) {
                sb.append(", ");
            }
            sb.append(this.data[i]);
        }
        sb.append("]");
        return sb.toString();
    }

    public static void main(String[] args) {
        ArrayStack stack = new ArrayStack();
        System.out.println(stack);
        stack.push(5);
        System.out.println(stack);
        stack.push(3);
        System.out.println(stack);
        System.out.println("-->" + stack.pop());
        System.out.println(stack);
        System.out.println("-->" + stack.peek());
        System.out.println(stack);
    }
}

