11. LinkedStack

  • The Stack was defined with an interface

  • An array based implementation of the stack was created — ArrayStack

  • Now, being aware of linked structures, a linked based stack may be implemented — LinkedStack

11.1. Stack ADT

  • The Stack interface was already written

  • Remember, based on the interface, a stack must implement the following methods

    • push

    • pop

    • peek

    • isEmpty

    • size

  • Also remember that the interface strictly defines the what and says nothing about the how

11.2. Implementing a Stack with a Linked Structure

  • The top of the stack will be managed by a field called top that is a reference to the head of a linked structure

  • All pushing and popping will happen at the head end of the linked structure

  • An additional field will be needed to keep track of the size of the stack

../../_images/linked_stack0.png

An example LinkedStack containing four elements. Note the value stored in top is a reference to a Node object that contains the element on the top of the stack.

../../_images/linked_stack1.png

The state of the LinkedStack after an element was pushed. Note that top changed to reference a new Node containing the newly pushed element.

../../_images/linked_stack2.png

The state of the LinkedStack after an element was popped. Note that top changed to reference the Node that was after the Node that contained the element on the top of the stack (the Node that was removed).

../../_images/linked_stack3.png

The state of the LinkedStack after another element was popped. Note that, again, top changed to reference the Node that was after the Node that contained the element on the top of the stack.

11.3. Implementation

 5/**
 6 * Implementation of a stack with a linked structure as the container. The class contains a static inner class defining
 7 * a node for the linked structure.
 8 *
 9 * @param <T> Type of elements that are to be in the stack
10 */
11public class LinkedStack<T> implements Stack<T> {
12
13    private Node<T> top;
14    private int size;
15
16    /**
17     * Create an empty LinkedStack.
18     */
19    public LinkedStack() {
20        top = null;
21        size = 0;
22    }
  • Like the ArrayStack, the LinkedStack will implement the Stack interface

  • The constructor creates an empty stack with nothing in it

11.3.1. Push

27    @Override
28    public boolean push(T element) {
29        Node<T> toPush = new Node<T>(element);
30        toPush.setNext(top);
31        top = toPush;
32        size++;
33        return true;
34    }
  • In push, notice how this is just adding to the front of a linked structure

11.3.2. Pop & Peek

38    @Override
39    public T pop() {
40        if (isEmpty()) {
41            throw new NoSuchElementException("Empty stack");
42        }
43        T returnElement = top.getData();
44        top = top.getNext();
45        size--;
46        return returnElement;
47    }
48
49    @Override
50    public T peek() {
51        if (isEmpty()) {
52            throw new NoSuchElementException("Empty stack");
53        }
54        return top.getData();
55    }
  • Like the ArrayStack, popping or peeking from an empty stack throws an exception

  • Notice how pop does a remove/delete from the front of a linked structure

11.3.3. size and isEmpty

59    @Override
60    public boolean isEmpty() {
61        return size() == 0;
62    }
63
64    @Override
65    public int size() {
66        return size;
67    }
  • The LinkedStack is empty if its size() == 0

    • How else could this condition be checked?

11.3.4. toString

71    @Override
72    public String toString() {
73        StringBuilder builder = new StringBuilder();
74        Node<T> currentNode = top;
75        while (currentNode != null) {
76            builder.append(currentNode.getData());
77            builder.append(", ");
78            currentNode = currentNode.getNext();
79        }
80        return builder.toString();
81    }
  • This is matching the output format that the ArrayStack's toString had

11.3.5. equals & hashCode

 85    @Override
 86    public boolean equals(Object o) {
 87        if (this == o) {
 88            return true;
 89        }
 90        if (o == null || getClass() != o.getClass()) {
 91            return false;
 92        }
 93        LinkedStack<?> that = (LinkedStack<?>) o;
 94        if (this.size() != that.size()) {
 95            return false;
 96        }
 97        Node<?> thisCurrent = this.top;
 98        Node<?> thatCurrent = that.top;
 99        while (thisCurrent != null && thatCurrent != null) {
100            if (!Objects.equals(thisCurrent.getData(), thatCurrent.getData())) {
101                return false;
102            }
103            thisCurrent = thisCurrent.getNext();
104            thatCurrent = thatCurrent.getNext();
105        }
106        return true;
107    }
108
109    @Override
110    public int hashCode() {
111        int result = Objects.hashCode(size);
112        Node<?> current = top;
113        while (current != null) {
114            result = result * 97 + Objects.hashCode(current.getData());
115            current = current.getNext();
116        }
117        return result;
118    }

11.4. Nested Node Class

  • Consider the doubly linked structure

../../_images/double_links.png
  • Since this structure need a reference to both the next and previous nodes, the existing Node class will not work

  • Also consider that the Node class would only be used for a linked implementation of something

    • As far as the user of a LinkedStack is concerned, they don’t care about the Node class, they just care that the LinkedStack works

      • I don’t know, I don’t want to know

    • Similar idea to the private method expandCapacity in the ArrayStack

  • Does it make sense to have the Node class accessible from everywhere?

11.4.1. Nested Classes

  • Thinking about the doubly linked structure, what should the node class be called, Node?

    • This would be a problem if there already exists a Node class

  • Instead, the Node class can be put inside the LinkedStack class

    • If this is done, the Node class can still be accessed by the LinkedStack class

    • But, it’s only accessible from within that class, so it keeps the Node class out of the way of all other classes

  • Going back to the doubly linked structure, a Node class can exist within the class using the doubly linked structure

    • It will not introduce any ambiguity since the singly linked and doubly linked structure’s Node classes are nested within their respective classes

  • Perhaps this is not that big of a problem, and there are other ways around it

  • But since the two classes are inextricably connected, nesting Node makes sense

11.4.2. Nesting in LinkedStack

  • Below is an example of the nested Node class at the end of the LinkedStack class

    • Notice that it is both private and static

122    /**
123     * A node class for a singly linked structure. Each node contains a nullable reference to data of type T, and a
124     * reference to the next/subsequent/successor singly linked node, which may be a null reference.
125     *
126     * @param <T> Type of the data being stored in the node
127     */
128    private static class Node<T> {
129
130        private T data;
131        private Node<T> next;
132
133        public Node() {
134            this(null);
135        }
136
137        public Node(T data) {
138            this.data = data;
139            this.next = null;
140        }
141
142        public T getData() {
143            return data;
144        }
145
146        public void setData(T data) {
147            this.data = data;
148        }
149
150        public Node<T> getNext() {
151            return next;
152        }
153
154        public void setNext(Node<T> next) {
155            this.next = next;
156        }
157    }

11.5. For Next Time

  • Read Chapter 4 Section 6

    • 13 pages

11.5.1. Playing Code

  • Download and play with

  • One could use the same code from PlayingArrayStack to play with the LinkedStack

  • Only need to make one change

    • ArrayStack -> LinkedStack

  • If everything was done correctly, the following code from PlayingLinkedStack should work

 1import java.util.NoSuchElementException;
 2
 3public class PlayingLinkedStack {
 4    public static void main(String[] args) {
 5        // Create a LinkedStack
 6        Stack<Integer> myStack = new LinkedStack<>();
 7
 8        // Check stack is empty
 9        System.out.println(myStack.size());
10        System.out.println(myStack.isEmpty());
11        System.out.println(myStack);
12
13        // Test push
14        myStack.push(0);
15        myStack.push(1);
16        myStack.push(2);
17        myStack.push(3);
18        myStack.push(4);
19        System.out.println(myStack.size());
20        System.out.println(myStack.isEmpty());
21        System.out.println(myStack);
22
23        // No test for expandCapacity since LinkedStack
24        // doesn't need it, but test more pushes anyways
25        // as the functionality does not change either way
26        myStack.push(10);
27        myStack.push(11);
28        myStack.push(12);
29        myStack.push(13);
30        myStack.push(14);
31        System.out.println(myStack.size());
32        System.out.println(myStack.isEmpty());
33        System.out.println(myStack);
34
35        // Test peek
36        System.out.println(myStack.peek());
37        System.out.println(myStack.size());
38        System.out.println(myStack.isEmpty());
39        System.out.println(myStack);
40
41        // Test Pop
42        System.out.println(myStack.pop());
43        System.out.println(myStack.pop());
44        System.out.println(myStack.pop());
45        System.out.println(myStack.pop());
46        System.out.println(myStack.pop());
47        System.out.println(myStack.pop());
48        System.out.println(myStack.pop());
49        System.out.println(myStack.pop());
50        System.out.println(myStack.pop());
51        System.out.println(myStack.pop());
52        System.out.println(myStack.size());
53        System.out.println(myStack.isEmpty());
54        System.out.println(myStack);
55
56        // Test peek and pop throwing exception
57        try {
58            myStack.peek();
59        } catch (NoSuchElementException e) {
60            e.printStackTrace();
61        }
62        try {
63            myStack.pop();
64        } catch (NoSuchElementException e) {
65            e.printStackTrace();
66        }
67    }
68}