diff --git a/src/main/java/com/thealgorithms/datastructures/resizablebuffer/ResizableCircularBuffer.java b/src/main/java/com/thealgorithms/datastructures/resizablebuffer/ResizableCircularBuffer.java new file mode 100644 index 000000000000..bcd24348adac --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/resizablebuffer/ResizableCircularBuffer.java @@ -0,0 +1,98 @@ +package com.thealgorithms.datastructures.resizablebuffer; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * The {@code ResizableCircularBuffer} class implements a generic resizable circular (or ring) buffer. + * This buffer grows automatically when it becomes full, retaining a FIFO (First In, First Out) structure. + * + * @param The type of elements stored in the circular buffer. + */ +public class ResizableCircularBuffer { + private Item[] buffer; + private int putPointer = 0; + private int getPointer = 0; + private final AtomicInteger size = new AtomicInteger(0); + + /** + * Constructor to initialize the circular buffer with a specified initial size. + * + * @param initialSize The initial size of the circular buffer. + * @throws IllegalArgumentException if the size is zero or negative. + */ + @SuppressWarnings("unchecked") + public ResizableCircularBuffer(int initialSize) { + if (initialSize <= 0) { + throw new IllegalArgumentException("Buffer size must be positive"); + } + this.buffer = (Item[]) new Object[initialSize]; + } + + /** + * Checks if the circular buffer is empty. + * + * @return {@code true} if the buffer is empty, {@code false} otherwise. + */ + public boolean isEmpty() { + return size.get() == 0; + } + + /** + * Checks if the circular buffer is full. + * + * @return {@code true} if the buffer is full, {@code false} otherwise. + */ + public boolean isFull() { + return size.get() == buffer.length; + } + + /** + * Retrieves and removes the item at the front of the buffer (FIFO). + * + * @return The item at the front of the buffer, or {@code null} if the buffer is empty. + */ + public synchronized Item get() { + if (isEmpty()) { + return null; + } + Item item = buffer[getPointer]; + buffer[getPointer] = null; // Optional: clear reference + getPointer = (getPointer + 1) % buffer.length; + size.decrementAndGet(); + return item; + } + + /** + * Adds an item to the end of the buffer (FIFO). If the buffer is full, it doubles in size. + * + * @param item The item to be added. + * @throws IllegalArgumentException if the item is null. + */ + public synchronized void put(Item item) { + if (item == null) { + throw new IllegalArgumentException("Null items are not allowed"); + } + + if (isFull()) { + resizeBuffer(); + } + + buffer[putPointer] = item; + putPointer = (putPointer + 1) % buffer.length; + size.incrementAndGet(); + } + + /** + * Resizes the buffer to twice its current size to accommodate more items. + */ + @SuppressWarnings("unchecked") + private void resizeBuffer() { + Item[] newBuffer = (Item[]) new Object[buffer.length * 2]; + for (int i = 0; i < buffer.length; i++) { + newBuffer[i] = buffer[(getPointer + i) % buffer.length]; + } + buffer = newBuffer; + getPointer = 0; + putPointer = size.get(); + } +} \ No newline at end of file diff --git a/src/test/java/com/thealgorithms/datastructures/resizablebuffer/ResizableCircularBufferTest.java b/src/test/java/com/thealgorithms/datastructures/resizablebuffer/ResizableCircularBufferTest.java new file mode 100644 index 000000000000..7a192c80cc52 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/resizablebuffer/ResizableCircularBufferTest.java @@ -0,0 +1,99 @@ +package com.thealgorithms.datastructures.resizablebuffer; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class ResizableCircularBufferTest { + + @Test + void testInitialization() { + ResizableCircularBuffer buffer = new ResizableCircularBuffer<>(5); + assertTrue(buffer.isEmpty()); + assertFalse(buffer.isFull()); + } + + @Test + void testPutAndGet() { + ResizableCircularBuffer buffer = new ResizableCircularBuffer<>(3); + + buffer.put("A"); + assertFalse(buffer.isEmpty()); + assertFalse(buffer.isFull()); + + buffer.put("B"); + buffer.put("C"); + assertTrue(buffer.isFull()); + + assertEquals("A", buffer.get()); + assertEquals("B", buffer.get()); + assertEquals("C", buffer.get()); + assertTrue(buffer.isEmpty()); + } + + @Test + void testResizeOnFullBuffer() { + ResizableCircularBuffer buffer = new ResizableCircularBuffer<>(2); + + buffer.put(1); + buffer.put(2); + buffer.put(3); // Should trigger resizing + + assertEquals(1, buffer.get()); + assertEquals(2, buffer.get()); + assertEquals(3, buffer.get()); + } + + @Test + void testOverwrite() { + ResizableCircularBuffer buffer = new ResizableCircularBuffer<>(2); + + buffer.put(1); + buffer.put(2); + buffer.put(3); // Should resize instead of overwrite + assertEquals(1, buffer.get()); + assertEquals(2, buffer.get()); + assertEquals(3, buffer.get()); + } + + @Test + void testEmptyBuffer() { + ResizableCircularBuffer buffer = new ResizableCircularBuffer<>(2); + assertNull(buffer.get()); + } + + @Test + void testFullBufferAndResize() { + ResizableCircularBuffer buffer = new ResizableCircularBuffer<>(2); + + buffer.put('A'); + buffer.put('B'); + assertTrue(buffer.isFull()); + + buffer.put('C'); // This should resize the buffer instead of overwriting 'A' + assertEquals('A', buffer.get()); + assertEquals('B', buffer.get()); + assertEquals('C', buffer.get()); + } + + @Test + void testIllegalArguments() { + assertThrows(IllegalArgumentException.class, () -> new ResizableCircularBuffer<>(0)); + assertThrows(IllegalArgumentException.class, () -> new ResizableCircularBuffer<>(-1)); + + ResizableCircularBuffer buffer = new ResizableCircularBuffer<>(1); + assertThrows(IllegalArgumentException.class, () -> buffer.put(null)); + } + + @Test + void testLargeBufferWithResize() { + ResizableCircularBuffer buffer = new ResizableCircularBuffer<>(10); + + // Fill buffer to trigger multiple resizes + for (int i = 0; i < 100; i++) { + buffer.put(i); + } + + assertEquals(10, buffer.get()); // Check that resizing kept order + } +} \ No newline at end of file