A ring buffer stores a fixed number of elements in a reusable array. Head identifies the next read slot; tail identifies the next write slot; count distinguishes empty from full when the indices coincide. Each enqueue and dequeue uses O(1) time without shifting elements. The API must choose whether a full buffer rejects new data, blocks a producer, or overwrites the oldest item. Those choices have different correctness effects. A telemetry sampler might overwrite old samples, while a payment queue must not silently discard a pending request.
Ring buffers: make capacity and overwrite rules explicit
Operational case
A sensor gateway reserves three slots for latest pressure readings. It writes 47, 52, and 61, reads 47, then writes 58 into the freed physical slot. Logical read order is now 52, 61, 58 even though physical array order wraps. The fourth write before a read would be rejected under this contract. The gateway might choose overwrite semantics for a dashboard, but that would need a different method name and a lost-sample counter. Capacity is part of the interface, not a hidden implementation setting.
Working Python program
capacity = 3
slots = [None] * capacity
head = tail = count = 0
for reading in (47, 52, 61):
slots[tail] = reading
tail = (tail + 1) % capacity
count += 1
oldest = slots[head]
head = (head + 1) % capacity
count -= 1
slots[tail] = 58
tail = (tail + 1) % capacity
count += 1
print(oldest, [slots[(head + offset) % capacity] for offset in range(count)])Output
47 [52, 61, 58]Time, space, and tradeoff
Storage is O(capacity), and each index update is O(1). The demonstration collects current readings in O(count) time only to display them. A real implementation should check count before every write and read; this short trace has known valid operations. Under concurrent producers and consumers, index changes need synchronization or a proven lock-free protocol. Modulo arithmetic is cheap relative to shifting a long list, but a zero capacity must be rejected before the first operation.
Common Mistakes
- Do not use head == tail alone to distinguish full from empty.
- Do not overwrite unread items unless the contract says so.
- Do not share mutable indices across threads without coordination.
Connected lessons
- Stacks and Queues
- DSA Tutorial
- Queues: preserve arrival order without front shifts
- Resizable arrays: account for growth and shifting
- Stacks: last-in-first-out for reversible edits
- Project: choose structures for a dispatch board
- Project: audit depot connectivity and daily ranges
- Linear and hash structure decisions
CLOCK caches: revisit pages through reference bits extends the retention comparison.
Hashed timing wheels: bucket incident expiries by tick extends the retention comparison.
