11. LinkedStack
The
Stackwas defined with an interfaceAn array based implementation of the stack was created —
ArrayStackNow, being aware of linked structures, a linked based stack may be implemented —
LinkedStack
11.1. Stack ADT
Remember, based on the interface, a stack must implement the following methods
pushpoppeekisEmptysize
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
topthat is a reference to the head of a linked structureAll 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
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.
The state of the LinkedStack after an element was pushed. Note that top changed to reference a new Node
containing the newly pushed element.
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).
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, theLinkedStackwill implement theStackinterfaceThe 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 exceptionNotice how
popdoes 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
LinkedStackis empty if itssize() == 0How 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'stoStringhad
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
Since this structure need a reference to both the
nextandpreviousnodes, the existingNodeclass will not workAlso consider that the
Nodeclass would only be used for a linked implementation of somethingAs far as the user of a
LinkedStackis concerned, they don’t care about theNodeclass, they just care that theLinkedStackworksI don’t know, I don’t want to know
Similar idea to the private method
expandCapacityin theArrayStack
Does it make sense to have the
Nodeclass 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
Nodeclass
Instead, the
Nodeclass can be put inside theLinkedStackclassIf this is done, the
Nodeclass can still be accessed by theLinkedStackclassBut, it’s only accessible from within that class, so it keeps the
Nodeclass out of the way of all other classes
Going back to the doubly linked structure, a
Nodeclass can exist within the class using the doubly linked structureIt will not introduce any ambiguity since the singly linked and doubly linked structure’s
Nodeclasses 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
Nodemakes sense
11.4.2. Nesting in LinkedStack
Below is an example of the nested
Nodeclass at the end of theLinkedStackclassNotice that it is both
privateandstatic
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
PlayingArrayStackto play with theLinkedStackOnly need to make one change
ArrayStack->LinkedStack
If everything was done correctly, the following code from
PlayingLinkedStackshould 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}