Merge pull request #9500 from priyank-sriv/bael-4145
BAEL-4145 Circular Buffer
This commit is contained in:
commit
32c2148c9f
data-structures/src
main/java/com/baeldung/circularbuffer
test/java/com/baeldung/circularbuffer
@ -0,0 +1,75 @@
|
||||
package com.baeldung.circularbuffer;
|
||||
|
||||
public class CircularBuffer<E> {
|
||||
|
||||
private static final int DEFAULT_CAPACITY = 8;
|
||||
|
||||
private final int capacity;
|
||||
private final E[] data;
|
||||
private volatile int writeSequence, readSequence;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public CircularBuffer(int capacity) {
|
||||
|
||||
this.capacity = (capacity < 1) ? DEFAULT_CAPACITY : capacity;
|
||||
this.data = (E[]) new Object[capacity];
|
||||
|
||||
this.readSequence = 0;
|
||||
this.writeSequence = -1;
|
||||
}
|
||||
|
||||
public boolean offer(E element) {
|
||||
|
||||
if (isNotFull()) {
|
||||
|
||||
int nextWriteSeq = writeSequence + 1;
|
||||
data[nextWriteSeq % capacity] = element;
|
||||
|
||||
writeSequence++;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public E poll() {
|
||||
|
||||
if (isNotEmpty()) {
|
||||
|
||||
E nextValue = data[readSequence % capacity];
|
||||
readSequence++;
|
||||
return nextValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int capacity() {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
|
||||
return (writeSequence - readSequence) + 1;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
|
||||
return writeSequence < readSequence;
|
||||
}
|
||||
|
||||
public boolean isFull() {
|
||||
|
||||
return size() >= capacity;
|
||||
}
|
||||
|
||||
private boolean isNotEmpty() {
|
||||
|
||||
return !isEmpty();
|
||||
}
|
||||
|
||||
private boolean isNotFull() {
|
||||
|
||||
return !isFull();
|
||||
}
|
||||
}
|
72
data-structures/src/test/java/com/baeldung/circularbuffer/CircularBufferUnitTest.java
Normal file
72
data-structures/src/test/java/com/baeldung/circularbuffer/CircularBufferUnitTest.java
Normal file
@ -0,0 +1,72 @@
|
||||
package com.baeldung.circularbuffer;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class CircularBufferUnitTest {
|
||||
|
||||
private final String[] shapes = { "Circle", "Triangle", "Rectangle", "Square", "Rhombus", "Trapezoid", "Pentagon", "Pentagram", "Hexagon", "Hexagram" };
|
||||
private final int defaultCapacity = shapes.length;
|
||||
|
||||
@Test
|
||||
public void givenCircularBuffer_whenAnElementIsEnqueued_thenSizeIsOne() {
|
||||
CircularBuffer<String> buffer = new CircularBuffer<>(defaultCapacity);
|
||||
|
||||
assertTrue(buffer.offer("Square"));
|
||||
assertEquals(1, buffer.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCircularBuffer_whenAnElementIsDequeued_thenElementMatchesEnqueuedElement() {
|
||||
CircularBuffer<String> buffer = new CircularBuffer<>(defaultCapacity);
|
||||
|
||||
buffer.offer("Triangle");
|
||||
|
||||
String shape = buffer.poll();
|
||||
assertEquals("Triangle", shape);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCircularBuffer_whenAnElementIsEnqueuedAndDeququed_thenBufferIsEmpty() {
|
||||
|
||||
CircularBuffer<String> buffer = new CircularBuffer<>(defaultCapacity);
|
||||
|
||||
buffer.offer("Rectangle");
|
||||
|
||||
assertFalse(buffer.isEmpty());
|
||||
assertEquals(1, buffer.size());
|
||||
|
||||
buffer.poll();
|
||||
|
||||
assertTrue(buffer.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCircularBuffer_whenFilledToCapacity_thenNoMoreElementsCanBeEnqueued() {
|
||||
|
||||
int capacity = shapes.length;
|
||||
CircularBuffer<String> buffer = new CircularBuffer<>(capacity);
|
||||
|
||||
assertTrue(buffer.isEmpty());
|
||||
|
||||
for (String shape : shapes) {
|
||||
buffer.offer(shape);
|
||||
}
|
||||
|
||||
assertTrue(buffer.isFull());
|
||||
assertFalse(buffer.offer("Octagon"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCircularBuffer_whenBufferIsEmpty_thenReturnsNull() {
|
||||
|
||||
CircularBuffer<String> buffer = new CircularBuffer<>(1);
|
||||
|
||||
assertTrue(buffer.isEmpty());
|
||||
assertNull(buffer.poll());
|
||||
}
|
||||
}
|
80
data-structures/src/test/java/com/baeldung/circularbuffer/ProducerConsumerLiveTest.java
Normal file
80
data-structures/src/test/java/com/baeldung/circularbuffer/ProducerConsumerLiveTest.java
Normal file
@ -0,0 +1,80 @@
|
||||
package com.baeldung.circularbuffer;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ProducerConsumerLiveTest {
|
||||
|
||||
private final String[] shapes = { "Circle", "Triangle", "Rectangle", "Square", "Rhombus", "Trapezoid", "Pentagon", "Pentagram", "Hexagon", "Hexagram" };
|
||||
|
||||
@Test
|
||||
public void givenACircularBuffer_whenInterleavingProducerConsumer_thenElementsMatch() throws Exception {
|
||||
CircularBuffer<String> buffer = new CircularBuffer<String>(shapes.length);
|
||||
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(2);
|
||||
|
||||
executorService.submit(new Producer<String>(buffer, shapes));
|
||||
Future<String[]> consumed = executorService.submit(new Consumer<String>(buffer, shapes.length));
|
||||
|
||||
String[] shapesConsumed = consumed.get(5L, TimeUnit.SECONDS);
|
||||
assertArrayEquals(shapes, shapesConsumed);
|
||||
}
|
||||
|
||||
static class Producer<T> implements Runnable {
|
||||
|
||||
private CircularBuffer<T> buffer;
|
||||
private T[] items;
|
||||
|
||||
public Producer(CircularBuffer<T> buffer, T[] items) {
|
||||
this.buffer = buffer;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
for (int i = 0; i < items.length;) {
|
||||
if (buffer.offer(items[i])) {
|
||||
System.out.println("Produced: " + items[i]);
|
||||
i++;
|
||||
LockSupport.parkNanos(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static class Consumer<T> implements Callable<T[]> {
|
||||
|
||||
private CircularBuffer<T> buffer;
|
||||
private int expectedCount;
|
||||
|
||||
public Consumer(CircularBuffer<T> buffer, int expectedCount) {
|
||||
this.buffer = buffer;
|
||||
this.expectedCount = expectedCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T[] call() throws Exception {
|
||||
T[] items = (T[]) new Object[expectedCount];
|
||||
for (int i = 0; i < items.length;) {
|
||||
T item = buffer.poll();
|
||||
if (item != null) {
|
||||
items[i++] = item;
|
||||
|
||||
LockSupport.parkNanos(5);
|
||||
System.out.println("Consumed: " + item);
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user