Merge pull request #9500 from priyank-sriv/bael-4145

BAEL-4145 Circular Buffer
This commit is contained in:
rpvilao 2020-06-26 17:09:22 +02:00 committed by GitHub
commit 32c2148c9f
3 changed files with 227 additions and 0 deletions

View File

@ -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();
}
}

View 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());
}
}

View 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;
}
}
}