Skip to content

Commit f5c0314

Browse files
authored
refactor: StackArray (#5349)
1 parent 8605220 commit f5c0314

File tree

3 files changed

+209
-127
lines changed

3 files changed

+209
-127
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.thealgorithms.datastructures.stacks;
2+
3+
/**
4+
* A generic interface for Stack data structures.
5+
*
6+
* @param <T> the type of elements in this stack
7+
*/
8+
public interface Stack<T> {
9+
10+
/**
11+
* Adds an element to the top of the stack.
12+
*
13+
* @param value The element to add.
14+
*/
15+
void push(T value);
16+
17+
/**
18+
* Removes the element at the top of this stack and returns it.
19+
*
20+
* @return The element popped from the stack.
21+
* @throws IllegalStateException if the stack is empty.
22+
*/
23+
T pop();
24+
25+
/**
26+
* Returns the element at the top of this stack without removing it.
27+
*
28+
* @return The element at the top of this stack.
29+
* @throws IllegalStateException if the stack is empty.
30+
*/
31+
T peek();
32+
33+
/**
34+
* Tests if this stack is empty.
35+
*
36+
* @return {@code true} if this stack is empty; {@code false} otherwise.
37+
*/
38+
boolean isEmpty();
39+
40+
/**
41+
* Returns the size of this stack.
42+
*
43+
* @return The number of elements in this stack.
44+
*/
45+
int size();
46+
47+
/**
48+
* Removes all elements from this stack.
49+
*/
50+
void makeEmpty();
51+
}

src/main/java/com/thealgorithms/datastructures/stacks/StackArray.java

+37-127
Original file line numberDiff line numberDiff line change
@@ -3,170 +3,80 @@
33
/**
44
* This class implements a Stack using a regular array.
55
*
6-
* <p>
7-
* A stack is exactly what it sounds like. An element gets added to the top of
8-
* the stack and only the element on the top may be removed. This is an example
9-
* of an array implementation of a Stack. So an element can only be
10-
* added/removed from the end of the array. In theory stack have no fixed size,
11-
* but with an array implementation it does.
6+
* @param <T> the type of elements in this stack
127
*/
13-
public class StackArray {
8+
public class StackArray<T> implements Stack<T> {
149

15-
/**
16-
* Driver Code
17-
*/
18-
public static void main(String[] args) {
19-
// Declare a stack of maximum size 4
20-
StackArray myStackArray = new StackArray(4);
21-
22-
assert myStackArray.isEmpty();
23-
assert !myStackArray.isFull();
24-
25-
// Populate the stack
26-
myStackArray.push(5);
27-
myStackArray.push(8);
28-
myStackArray.push(2);
29-
myStackArray.push(9);
30-
31-
assert !myStackArray.isEmpty();
32-
assert myStackArray.isFull();
33-
assert myStackArray.peek() == 9;
34-
assert myStackArray.pop() == 9;
35-
assert myStackArray.peek() == 2;
36-
assert myStackArray.size() == 3;
37-
}
38-
39-
/**
40-
* Default initial capacity.
41-
*/
4210
private static final int DEFAULT_CAPACITY = 10;
4311

44-
/**
45-
* The max size of the Stack
46-
*/
4712
private int maxSize;
48-
49-
/**
50-
* The array representation of the Stack
51-
*/
52-
private int[] stackArray;
53-
54-
/**
55-
* The top of the stack
56-
*/
13+
private T[] stackArray;
5714
private int top;
5815

59-
/**
60-
* init Stack with DEFAULT_CAPACITY
61-
*/
16+
@SuppressWarnings("unchecked")
6217
public StackArray() {
6318
this(DEFAULT_CAPACITY);
6419
}
6520

66-
/**
67-
* Constructor
68-
*
69-
* @param size Size of the Stack
70-
*/
21+
@SuppressWarnings("unchecked")
7122
public StackArray(int size) {
72-
maxSize = size;
73-
stackArray = new int[maxSize];
74-
top = -1;
23+
if (size <= 0) {
24+
throw new IllegalArgumentException("Stack size must be greater than 0");
25+
}
26+
this.maxSize = size;
27+
this.stackArray = (T[]) new Object[size];
28+
this.top = -1;
7529
}
7630

77-
/**
78-
* Adds an element to the top of the stack
79-
*
80-
* @param value The element added
81-
*/
82-
public void push(int value) {
83-
if (!isFull()) { // Checks for a full stack
84-
top++;
85-
stackArray[top] = value;
86-
} else {
31+
@Override
32+
public void push(T value) {
33+
if (isFull()) {
8734
resize(maxSize * 2);
88-
push(value); // don't forget push after resizing
8935
}
36+
stackArray[++top] = value;
9037
}
9138

92-
/**
93-
* Removes the top element of the stack and returns the value you've removed
94-
*
95-
* @return value popped off the Stack
96-
*/
97-
public int pop() {
98-
if (!isEmpty()) { // Checks for an empty stack
99-
return stackArray[top--];
39+
@Override
40+
public T pop() {
41+
if (isEmpty()) {
42+
throw new IllegalStateException("Stack is empty, cannot pop element");
10043
}
101-
102-
if (top < maxSize / 4) {
44+
T value = stackArray[top--];
45+
if (top + 1 < maxSize / 4 && maxSize > DEFAULT_CAPACITY) {
10346
resize(maxSize / 2);
104-
return pop(); // don't forget pop after resizing
105-
} else {
106-
System.out.println("The stack is already empty");
107-
return -1;
10847
}
48+
return value;
10949
}
11050

111-
/**
112-
* Returns the element at the top of the stack
113-
*
114-
* @return element at the top of the stack
115-
*/
116-
public int peek() {
117-
if (!isEmpty()) { // Checks for an empty stack
118-
return stackArray[top];
119-
} else {
120-
System.out.println("The stack is empty, cant peek");
121-
return -1;
51+
@Override
52+
public T peek() {
53+
if (isEmpty()) {
54+
throw new IllegalStateException("Stack is empty, cannot peek element");
12255
}
56+
return stackArray[top];
12357
}
12458

12559
private void resize(int newSize) {
126-
int[] transferArray = new int[newSize];
127-
128-
for (int i = 0; i < stackArray.length; i++) {
129-
transferArray[i] = stackArray[i];
130-
}
131-
// This reference change might be nice in here
132-
stackArray = transferArray;
60+
@SuppressWarnings("unchecked") T[] newArray = (T[]) new Object[newSize];
61+
System.arraycopy(stackArray, 0, newArray, 0, top + 1);
62+
stackArray = newArray;
13363
maxSize = newSize;
13464
}
13565

136-
/**
137-
* Returns true if the stack is empty
138-
*
139-
* @return true if the stack is empty
140-
*/
141-
public boolean isEmpty() {
142-
return (top == -1);
66+
public boolean isFull() {
67+
return top + 1 == maxSize;
14368
}
14469

145-
/**
146-
* Returns true if the stack is full
147-
*
148-
* @return true if the stack is full
149-
*/
150-
public boolean isFull() {
151-
return (top + 1 == maxSize);
70+
@Override
71+
public boolean isEmpty() {
72+
return top == -1;
15273
}
15374

154-
/**
155-
* Deletes everything in the Stack
156-
*
157-
* <p>
158-
* Doesn't delete elements in the array but if you call push method after
159-
* calling makeEmpty it will overwrite previous values
160-
*/
161-
public void makeEmpty() { // Doesn't delete elements in the array but if you call
75+
@Override public void makeEmpty() { // Doesn't delete elements in the array but if you call
16276
top = -1; // push method after calling makeEmpty it will overwrite previous values
16377
}
16478

165-
/**
166-
* Return size of stack
167-
*
168-
* @return size of stack
169-
*/
79+
@Override
17080
public int size() {
17181
return top + 1;
17282
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package com.thealgorithms.datastructures.stacks;
2+
3+
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.BeforeEach;
5+
import org.junit.jupiter.api.Test;
6+
7+
class StackArrayTest {
8+
9+
private Stack<Integer> stack;
10+
11+
@BeforeEach
12+
void setUp() {
13+
stack = new StackArray<>(5); // Initialize a stack with capacity of 5
14+
}
15+
16+
@Test
17+
void testPushAndPop() {
18+
stack.push(1);
19+
stack.push(2);
20+
stack.push(3);
21+
stack.push(4);
22+
stack.push(5);
23+
24+
Assertions.assertEquals(5, stack.pop()); // Stack follows LIFO, so 5 should be popped first
25+
Assertions.assertEquals(4, stack.pop()); // Next, 4 should be popped
26+
Assertions.assertEquals(3, stack.pop()); // Followed by 3
27+
Assertions.assertEquals(2, stack.pop()); // Then 2
28+
Assertions.assertEquals(1, stack.pop()); // Finally 1
29+
}
30+
31+
@Test
32+
void testPeek() {
33+
stack.push(10);
34+
stack.push(20);
35+
stack.push(30);
36+
37+
Assertions.assertEquals(30, stack.peek()); // Peek should return 30, the top of the stack
38+
Assertions.assertEquals(3, stack.size()); // Size should remain 3 after peek
39+
40+
stack.pop();
41+
Assertions.assertEquals(20, stack.peek()); // After popping, peek should return 20
42+
}
43+
44+
@Test
45+
void testIsEmpty() {
46+
Assertions.assertTrue(stack.isEmpty()); // Initially, the stack should be empty
47+
stack.push(42);
48+
Assertions.assertFalse(stack.isEmpty()); // After pushing an element, the stack should not be empty
49+
stack.pop();
50+
Assertions.assertTrue(stack.isEmpty()); // After popping the only element, the stack should be empty again
51+
}
52+
53+
@Test
54+
void testResizeOnPush() {
55+
StackArray<Integer> smallStack = new StackArray<>(2); // Start with a small stack size
56+
smallStack.push(1);
57+
smallStack.push(2);
58+
Assertions.assertTrue(smallStack.isFull()); // Initially, the stack should be full
59+
60+
smallStack.push(3); // This push should trigger a resize
61+
Assertions.assertFalse(smallStack.isFull()); // The stack should no longer be full after resize
62+
Assertions.assertEquals(3, smallStack.size()); // Size should be 3 after pushing 3 elements
63+
64+
Assertions.assertEquals(3, smallStack.pop()); // LIFO behavior check
65+
Assertions.assertEquals(2, smallStack.pop());
66+
Assertions.assertEquals(1, smallStack.pop());
67+
}
68+
69+
@Test
70+
void testResizeOnPop() {
71+
StackArray<Integer> stack = new StackArray<>(4);
72+
stack.push(1);
73+
stack.push(2);
74+
stack.push(3);
75+
stack.push(4);
76+
77+
stack.pop(); // Removing elements should trigger a resize when less than 1/4 of the stack is used
78+
stack.pop();
79+
stack.pop();
80+
Assertions.assertEquals(1, stack.size()); // After popping, only one element should remain
81+
82+
stack.pop();
83+
Assertions.assertTrue(stack.isEmpty()); // The stack should be empty now
84+
}
85+
86+
@Test
87+
void testMakeEmpty() {
88+
stack.push(1);
89+
stack.push(2);
90+
stack.push(3);
91+
stack.makeEmpty();
92+
93+
Assertions.assertTrue(stack.isEmpty()); // The stack should be empty after calling makeEmpty
94+
Assertions.assertThrows(IllegalStateException.class, stack::pop); // Popping from empty stack should throw exception
95+
}
96+
97+
@Test
98+
void testPopEmptyStackThrowsException() {
99+
Assertions.assertThrows(IllegalStateException.class, stack::pop); // Popping from an empty stack should throw an exception
100+
}
101+
102+
@Test
103+
void testPeekEmptyStackThrowsException() {
104+
Assertions.assertThrows(IllegalStateException.class, stack::peek); // Peeking into an empty stack should throw an exception
105+
}
106+
107+
@Test
108+
void testConstructorWithInvalidSizeThrowsException() {
109+
Assertions.assertThrows(IllegalArgumentException.class, () -> new StackArray<>(0)); // Size 0 is invalid
110+
Assertions.assertThrows(IllegalArgumentException.class, () -> new StackArray<>(-5)); // Negative size is invalid
111+
}
112+
113+
@Test
114+
void testDefaultConstructor() {
115+
StackArray<Integer> defaultStack = new StackArray<>(); // Using default constructor
116+
Assertions.assertEquals(0, defaultStack.size()); // Initially, size should be 0
117+
118+
defaultStack.push(1);
119+
Assertions.assertEquals(1, defaultStack.size()); // After pushing, size should be 1
120+
}
121+
}

0 commit comments

Comments
 (0)