-
Notifications
You must be signed in to change notification settings - Fork 19.4k
/
CircularBuffer.java
132 lines (119 loc) · 4.59 KB
/
CircularBuffer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package com.thealgorithms.datastructures.buffers;
import java.util.concurrent.atomic.AtomicInteger;
/**
* The {@code CircularBuffer} class implements a generic circular (or ring) buffer.
* A circular buffer is a fixed-size data structure that operates in a FIFO (First In, First Out) manner.
* The buffer allows you to overwrite old data when the buffer is full and efficiently use limited memory.
* When the buffer is full, adding a new item will overwrite the oldest data.
*
* @param <Item> The type of elements stored in the circular buffer.
*/
public class CircularBuffer<Item> {
private final Item[] buffer;
private final CircularPointer putPointer;
private final CircularPointer getPointer;
private final AtomicInteger size = new AtomicInteger(0);
/**
* Constructor to initialize the circular buffer with a specified size.
*
* @param size The size of the circular buffer.
* @throws IllegalArgumentException if the size is zero or negative.
*/
public CircularBuffer(int size) {
if (size <= 0) {
throw new IllegalArgumentException("Buffer size must be positive");
}
// noinspection unchecked
this.buffer = (Item[]) new Object[size];
this.putPointer = new CircularPointer(0, size);
this.getPointer = new CircularPointer(0, size);
}
/**
* Checks if the circular buffer is empty.
* This method is based on the current size of the buffer.
*
* @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.
* The buffer is considered full when its size equals its capacity.
*
* @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).
* This operation will move the {@code getPointer} forward.
*
* @return The item at the front of the buffer, or {@code null} if the buffer is empty.
*/
public Item get() {
if (isEmpty()) {
return null;
}
Item item = buffer[getPointer.getAndIncrement()];
size.decrementAndGet();
return item;
}
/**
* Adds an item to the end of the buffer (FIFO).
* If the buffer is full, this operation will overwrite the oldest data.
*
* @param item The item to be added.
* @throws IllegalArgumentException if the item is null.
* @return {@code true} if the item was successfully added, {@code false} if the buffer was full and the item overwrote existing data.
*/
public boolean put(Item item) {
if (item == null) {
throw new IllegalArgumentException("Null items are not allowed");
}
boolean wasEmpty = isEmpty();
if (isFull()) {
getPointer.getAndIncrement(); // Move get pointer to discard oldest item
} else {
size.incrementAndGet();
}
buffer[putPointer.getAndIncrement()] = item;
return wasEmpty;
}
/**
* The {@code CircularPointer} class is a helper class used to track the current index (pointer)
* in the circular buffer.
* The max value represents the capacity of the buffer.
* The `CircularPointer` class ensures that the pointer automatically wraps around to 0
* when it reaches the maximum index.
* This is achieved in the `getAndIncrement` method, where the pointer
* is incremented and then taken modulo the maximum value (`max`).
* This operation ensures that the pointer always stays within the bounds of the buffer.
*/
private static class CircularPointer {
private int pointer;
private final int max;
/**
* Constructor to initialize the circular pointer.
*
* @param pointer The initial position of the pointer.
* @param max The maximum size (capacity) of the circular buffer.
*/
CircularPointer(int pointer, int max) {
this.pointer = pointer;
this.max = max;
}
/**
* Increments the pointer by 1 and wraps it around to 0 if it reaches the maximum value.
* This ensures the pointer always stays within the buffer's bounds.
*
* @return The current pointer value before incrementing.
*/
public int getAndIncrement() {
int tmp = pointer;
pointer = (pointer + 1) % max;
return tmp;
}
}
}