Stack with "getMiddle" and "deleteMiddle" operation Looking for code review, optimizations and best practices.

public class FindMiddleStack&lt;T&gt; {

    private Node&lt;T&gt; top;
    private Node&lt;T&gt; middle;
    private int size = 0;

    private static class Node&lt;T&gt; {
        Node&lt;T&gt; left;
        T item;
        Node&lt;T&gt; right;

        Node(Node&lt;T&gt; left, T item, Node&lt;T&gt; next) {
            this.left =  left;
            this.item = item;
            this.right = next;
        }
    }


    public void push(T item) {
        final Node&lt;T&gt; node = new Node&lt;T&gt;(null, item, top);
        if (top == null) {
            top = node;
            middle = node;
        } else {
            top.left = node;
            top = node;
        }

        size++;
        if (size % 2 == 0) {
            middle = middle.left;
        }
    }


    public T pop() {
        if (top == null) {
            throw new EmptyStackException();
        }
        T item = top.item;
        top = top.right;
        // important, to not leak references.
        if (top != null) {
            top.left = null;
        }

        if (top == null) {
            middle = null;
        }

        if (size % 2 == 0) {
            middle = middle.right;
        }

        size--;
        return item;
    }

    public T peek() {
        if (top == null) {
            throw new EmptyStackException();
        }
        return top.item;
    }


    public T getMiddle() {
        if (top == null) {
            throw new EmptyStackException();
        }
        return middle.item;
    }


    public void deleteMiddle() {
        if (top == null) {
            throw new EmptyStackException();
        }

        if (middle.left != null) {
            middle.left.right = middle.right;
        }
        if (middle.right != null) {
            middle.right.left = middle.left;
        }

        if (size % 2 == 1) {
            // deleted middle.
            middle = middle.left;
        } else {
            middle = middle.right;
        }

        if (middle == null) {
            top = null;
        }
        size--;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }
}


public class FindMiddleStackTest {

    @Test
    public void testPushPop() {
        FindMiddleStack&lt;Integer&gt; fm = new FindMiddleStack&lt;Integer&gt;();
        fm.push(10);
        fm.push(20);
        fm.push(30);
        fm.push(40);
        fm.push(50);
        fm.push(60);
        fm.push(70);
        int[] expected1 = {70, 60, 50, 40, 30, 20, 10};
        int[] actual1 = new int[7];
        int i1 = 0;
        while (!fm.isEmpty()) {
            actual1[i1++] = fm.pop();
        }
        assertTrue(Arrays.equals(expected1, actual1));
    }


    @Test
    public void testPopAndMiddle( ) {
        FindMiddleStack&lt;Integer&gt; fm = new FindMiddleStack&lt;Integer&gt;();
        fm.push(10);
        fm.push(20);
        fm.push(30);
        fm.push(40);
        fm.push(50);
        fm.push(60);
        fm.push(70);
        int[] expected2 = {40, 40, 30, 30, 20, 20, 10};
        int[] actual2 = new int[7];
        int i2 = 0;
        while (!fm.isEmpty()) {
            actual2[i2++] = fm.getMiddle();
            fm.pop();
        }
        assertTrue(Arrays.equals(expected2, actual2));
    }

    @Test
    public void testDeleteMiddleAndMiddle() {
        FindMiddleStack&lt;Integer&gt; fm = new FindMiddleStack&lt;Integer&gt;();
        fm.push(10);
        fm.push(20);
        fm.push(30);
        fm.push(40);
        fm.push(50);
        fm.push(60);
        fm.push(70);
        int[] expected3 = {40, 50, 30, 60, 20, 70, 10};
        int[] actual3 = new int[7];
        int i3 = 0;
        while (!fm.isEmpty()) {
            actual3[i3++] =  fm.getMiddle();
            fm.deleteMiddle();
        }
        assertTrue(Arrays.equals(expected3, actual3));
    }
}
Implementing "correctly":

Your Classname says, you have implemented a Stack with additional operations. Why do I not see implements or extends in your code then?


public class FindMiddleStack&lt;T&gt; {



should have been:

public class FindMiddleStack&lt;T&gt; extends Stack&lt;T&gt; {


scratch that, Stack&lt;T&gt; has been deprecated for so long, that there should be some annotation @Antique. Even the official oracle documentation recommends the use of Deque&lt;T&gt;!
Mind that Deque can also be used as a FIFO Queue

That said, your class could start with the following:

public class FindMiddleDeque&lt;T&gt; implements Deque&lt;T&gt; {


Design:

Your node class design is "flawed". You make it way to complicated, if you only want a LIFO (last in first out) behavior. Usually a Node in a stack knows about it's value and has a reference to the next Node.

private static class Node&lt;T&gt; {
    Node&lt;T&gt; next;
    T value;

    Node(T value, Node&lt;T&gt; next) {
        this.value = value;
        this.next = next;
    }
}


This also simplifies your pop and push methods:

@Override
public T pop() {
    if(size == 0 || top == null){
       throw new EmptyStackException();
    }

    size--;
    Node&lt;T&gt; destroy = top;
    top = destroy.next;

    return destroy.value;
    //destroy should be destroyed by falling out of scope
}

@Override
public void push(T value) {
    top = new Node(value, top);
    size++;
}

@Override
public T peek() {
    if(size == 0 || top == null){
       throw new EmptyStackException();
    }

    return top.value;
}


Finding the middle.

Your code is a little complicated concerning finding the middle of the stack. In fact we can assume, that the middle element is that at "index" size / 2. 

How can we use that to our advantage?

public void deleteMiddle() {
    if(size == 0 || top == null){
        throw new EmptyStackException();
    }
    int middleIndex = size / 2;

    Node&lt;T&gt; current = top;
    Node&lt;T&gt; previous = null;
    for (int i = 0; i &lt; middleIndex; i++){
        previous = current;
        current = current.next;
    }
    // we can assume we are in the middle now.
    previous.next = current.next; //current is lost
    size--;
}


We can also get the middle element in a similar fashion:

public T getMiddle() {
    if(size == 0 || top == null){
        throw new EmptyStackException();
    }

    int middleIndex = size / 2;

    Node&lt;T&gt; current = top;
    for (int i = 0; i &lt; middleIndex; i++){
        current = current.next;
    }
    return current.value;
}