A deque is an abstract data type, because it is defined by what you can do with it; what operations it supports. You can add and remove elements at either end of a deque.
A RingBuffer is a data structure, because it defined by how it is represented in memory, and how its state should be manipulated to fulfil the deque operations.
This implementation should be avoided; it is weird, buggy, and unnecessarily complicated.
In particular, using some pseudocode to simplify:
rb = new RingBuffer(... capacity=3, orderedReads=false/*the default*/)
rb.put_all(A, B, C, D)
rb.get() // D
rb.put(E)
rb.get_all(3) // E, C, D again
I haven't looked for bugs with orderedReads=true (besides the obvious memory leak), but given the footguns and unnecessarily complications this should be avoided regardless.
That's about the least-bad thing about the code here - it's a workaround for the fact that Java lacks runtime support for generics.
Other than its indirect use in `copy` (which in Java normally takes the parameter itself), it's probably not actually necessary here since you could just use `Object[]`? But I don't spend enough time in Java (thankfully) so I'm not sure if there are intricacies involving the runtime overhead of casting.
Looking at the source, I don't think there's an actual need for the constructor to take a `Class<T>` type. It's used internally to initialize an array [1]:
However, I think this alternative would work equally well, and would not require a `Class<T>` parameter:
this.entries = (T[]) new Object[capacity];
There are some other choices in the library that I don't understand, such as the choice to have a static constructor with this signature:
public static RingBuffer<Object> create(final int capacity) {
return create(capacity, false);
}
This returns the type `RingBuffer<Object>`, which isn't as useful as it could be; with appropriate idiomatic use of generics it could return a `RingBuffer<T>`:
public static <T> RingBuffer<T> create(final int capacity, final boolean orderedReads) {
return new RingBuffer<>(capacity, orderedReads);
}
It's possible that this code was written by someone who is still learning idiomatic Java style, or effective use of generics.
I'm also curious about the choice to have `get()` return `null`. I think I'd rather have seen this modeled with `Optional`. My preferred style when writing Java code is to employ non-nullable references wherever possible (though the return from `get()` is marked `@Nullable` at least).
I wrote this guy a few years back. It's lock free for both consumers and producers. Blocking variants are also available, but with significantly poorer performance.
15 comments
[ 2.5 ms ] story [ 42.5 ms ] threadA RingBuffer is a data structure, because it defined by how it is represented in memory, and how its state should be manipulated to fulfil the deque operations.
Aren't there concurrent versions of this that block writers until space frees up?
In particular, using some pseudocode to simplify:
I haven't looked for bugs with orderedReads=true (besides the obvious memory leak), but given the footguns and unnecessarily complications this should be avoided regardless.Other than its indirect use in `copy` (which in Java normally takes the parameter itself), it's probably not actually necessary here since you could just use `Object[]`? But I don't spend enough time in Java (thankfully) so I'm not sure if there are intricacies involving the runtime overhead of casting.
I'm also curious about the choice to have `get()` return `null`. I think I'd rather have seen this modeled with `Optional`. My preferred style when writing Java code is to employ non-nullable references wherever possible (though the return from `get()` is marked `@Nullable` at least).
[1] https://github.com/evolvedbinary/j8cu/blob/94d64cfc0ec49a340...
You can't just say new T[capacity] in java.
https://github.com/asgeirn/circular-buffer