Skip to content

Commit 91baea0

Browse files
committed
Added resizable circular buffer and unit tests for buffer resizing and edge cases
1 parent bca8d0e commit 91baea0

File tree

2 files changed

+197
-0
lines changed

2 files changed

+197
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package com.thealgorithms.datastructures.resizablebuffer;
2+
3+
import java.util.concurrent.atomic.AtomicInteger;
4+
5+
/**
6+
* The {@code ResizableCircularBuffer} class implements a generic resizable circular (or ring) buffer.
7+
* This buffer grows automatically when it becomes full, retaining a FIFO (First In, First Out) structure.
8+
*
9+
* @param <Item> The type of elements stored in the circular buffer.
10+
*/
11+
public class ResizableCircularBuffer<Item> {
12+
private Item[] buffer;
13+
private int putPointer = 0;
14+
private int getPointer = 0;
15+
private final AtomicInteger size = new AtomicInteger(0);
16+
17+
/**
18+
* Constructor to initialize the circular buffer with a specified initial size.
19+
*
20+
* @param initialSize The initial size of the circular buffer.
21+
* @throws IllegalArgumentException if the size is zero or negative.
22+
*/
23+
@SuppressWarnings("unchecked")
24+
public ResizableCircularBuffer(int initialSize) {
25+
if (initialSize <= 0) {
26+
throw new IllegalArgumentException("Buffer size must be positive");
27+
}
28+
this.buffer = (Item[]) new Object[initialSize];
29+
}
30+
31+
/**
32+
* Checks if the circular buffer is empty.
33+
*
34+
* @return {@code true} if the buffer is empty, {@code false} otherwise.
35+
*/
36+
public boolean isEmpty() {
37+
return size.get() == 0;
38+
}
39+
40+
/**
41+
* Checks if the circular buffer is full.
42+
*
43+
* @return {@code true} if the buffer is full, {@code false} otherwise.
44+
*/
45+
public boolean isFull() {
46+
return size.get() == buffer.length;
47+
}
48+
49+
/**
50+
* Retrieves and removes the item at the front of the buffer (FIFO).
51+
*
52+
* @return The item at the front of the buffer, or {@code null} if the buffer is empty.
53+
*/
54+
public synchronized Item get() {
55+
if (isEmpty()) {
56+
return null;
57+
}
58+
Item item = buffer[getPointer];
59+
buffer[getPointer] = null; // Optional: clear reference
60+
getPointer = (getPointer + 1) % buffer.length;
61+
size.decrementAndGet();
62+
return item;
63+
}
64+
65+
/**
66+
* Adds an item to the end of the buffer (FIFO). If the buffer is full, it doubles in size.
67+
*
68+
* @param item The item to be added.
69+
* @throws IllegalArgumentException if the item is null.
70+
*/
71+
public synchronized void put(Item item) {
72+
if (item == null) {
73+
throw new IllegalArgumentException("Null items are not allowed");
74+
}
75+
76+
if (isFull()) {
77+
resizeBuffer();
78+
}
79+
80+
buffer[putPointer] = item;
81+
putPointer = (putPointer + 1) % buffer.length;
82+
size.incrementAndGet();
83+
}
84+
85+
/**
86+
* Resizes the buffer to twice its current size to accommodate more items.
87+
*/
88+
@SuppressWarnings("unchecked")
89+
private void resizeBuffer() {
90+
Item[] newBuffer = (Item[]) new Object[buffer.length * 2];
91+
for (int i = 0; i < buffer.length; i++) {
92+
newBuffer[i] = buffer[(getPointer + i) % buffer.length];
93+
}
94+
buffer = newBuffer;
95+
getPointer = 0;
96+
putPointer = size.get();
97+
}
98+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.thealgorithms.datastructures.resizablebuffer;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
class ResizableCircularBufferTest {
8+
9+
@Test
10+
void testInitialization() {
11+
ResizableCircularBuffer<Integer> buffer = new ResizableCircularBuffer<>(5);
12+
assertTrue(buffer.isEmpty());
13+
assertFalse(buffer.isFull());
14+
}
15+
16+
@Test
17+
void testPutAndGet() {
18+
ResizableCircularBuffer<String> buffer = new ResizableCircularBuffer<>(3);
19+
20+
buffer.put("A");
21+
assertFalse(buffer.isEmpty());
22+
assertFalse(buffer.isFull());
23+
24+
buffer.put("B");
25+
buffer.put("C");
26+
assertTrue(buffer.isFull());
27+
28+
assertEquals("A", buffer.get());
29+
assertEquals("B", buffer.get());
30+
assertEquals("C", buffer.get());
31+
assertTrue(buffer.isEmpty());
32+
}
33+
34+
@Test
35+
void testResizeOnFullBuffer() {
36+
ResizableCircularBuffer<Integer> buffer = new ResizableCircularBuffer<>(2);
37+
38+
buffer.put(1);
39+
buffer.put(2);
40+
buffer.put(3); // Should trigger resizing
41+
42+
assertEquals(1, buffer.get());
43+
assertEquals(2, buffer.get());
44+
assertEquals(3, buffer.get());
45+
}
46+
47+
@Test
48+
void testOverwrite() {
49+
ResizableCircularBuffer<Integer> buffer = new ResizableCircularBuffer<>(2);
50+
51+
buffer.put(1);
52+
buffer.put(2);
53+
buffer.put(3); // Should resize instead of overwrite
54+
assertEquals(1, buffer.get());
55+
assertEquals(2, buffer.get());
56+
assertEquals(3, buffer.get());
57+
}
58+
59+
@Test
60+
void testEmptyBuffer() {
61+
ResizableCircularBuffer<Double> buffer = new ResizableCircularBuffer<>(2);
62+
assertNull(buffer.get());
63+
}
64+
65+
@Test
66+
void testFullBufferAndResize() {
67+
ResizableCircularBuffer<Character> buffer = new ResizableCircularBuffer<>(2);
68+
69+
buffer.put('A');
70+
buffer.put('B');
71+
assertTrue(buffer.isFull());
72+
73+
buffer.put('C'); // This should resize the buffer instead of overwriting 'A'
74+
assertEquals('A', buffer.get());
75+
assertEquals('B', buffer.get());
76+
assertEquals('C', buffer.get());
77+
}
78+
79+
@Test
80+
void testIllegalArguments() {
81+
assertThrows(IllegalArgumentException.class, () -> new ResizableCircularBuffer<>(0));
82+
assertThrows(IllegalArgumentException.class, () -> new ResizableCircularBuffer<>(-1));
83+
84+
ResizableCircularBuffer<String> buffer = new ResizableCircularBuffer<>(1);
85+
assertThrows(IllegalArgumentException.class, () -> buffer.put(null));
86+
}
87+
88+
@Test
89+
void testLargeBufferWithResize() {
90+
ResizableCircularBuffer<Integer> buffer = new ResizableCircularBuffer<>(10);
91+
92+
// Fill buffer to trigger multiple resizes
93+
for (int i = 0; i < 100; i++) {
94+
buffer.put(i);
95+
}
96+
97+
assertEquals(10, buffer.get()); // Check that resizing kept order
98+
}
99+
}

0 commit comments

Comments
 (0)