0

In terms of big-O runtime, it seems that both data structures have in the "average" case:

  • O(1) insertion/removal into the start and end
  • O(n) insertion/removal into some arbitrary index
  • O(1) lookup of the start and end

Advantages of circular buffer:

  • O(1) lookup instead of O(n) of some arbitrary index
  • Doesn't need to create nodes, thus doesn't need a dynamic allocation on each insertion
  • Faster traversal due to better cache prediction
  • Faster removal due to vectorization (e.g. using memmove) to fill the gap
  • Typically needs less space (because in a linked list, for each node, you have to sort pointers to the next and/or previous node)

Advantages of linked list:

  • Easier to get O(1) insertion/removal to some specific place (e.g., could get it for midway through the linked list). Circular buffers can do it, but it's more complicated
  • O(1) insertion in the worst case, unlike circular buffers that are O(n) (when it needs to grow the buffer)

Based on this list, it seems to me that circular buffers are a far better choice in almost every case. Am I missing something?

meisel
  • 2,151
  • 2
  • 21
  • 38
  • `O(1) lookup instead of O(n) of some arbitrary index` -- Uh, no. Why? It's still O(n), just for a smaller (constrained) n. If you're comparing a linked list to a circular buffer implemented with an array, then you're comparing apples and oranges. – Robert Harvey Dec 10 '18 at 16:16
  • And here we get to the rub, the reason collections are preferred over arrays in almost every case except one: *collections are not of fixed size like arrays are.* – Robert Harvey Dec 10 '18 at 16:17
  • To your first comment, circular buffers are always implemented with an array, by definition: https://en.wikipedia.org/wiki/Circular_buffer . I don't see why you're saying it's apples and oranges, these are both data structures. To your second comment, and assuming that by "collection" and "array" you're referring to those terms in Java, these are both growable collections, one of which uses an array internally. – meisel Dec 10 '18 at 17:24
  • `these are both data structures.` -- Sure, and a grass hut and an apartment in Manhattan are both dwellings. – Robert Harvey Dec 10 '18 at 17:25
  • I suggest you tie your post to a specific software development problem you're having. Otherwise, it's just a discussion that qualifies for closure as too broad or opinion-based. – Robert Harvey Dec 10 '18 at 17:27
  • Anyway, you already know the answer to this. You choose the data structure that is the best fit for your specific application's requirements, from a performance/memory standpoint. There is no definitive "best." Ring buffers are good for things like lossy real-time communication channels. They're also good for standing up lock-free multiprocessing scenarios; see LMAX Disruptor. Collections are good for... Well, you already know that too. – Robert Harvey Dec 10 '18 at 17:39
  • A ring buffer is one possible implementation of a collection... – meisel Dec 10 '18 at 18:27

1 Answers1

1

The MCS lock is one of the most scalable lock designs there is. A thread uses an atomic compare and exchange to attempt to seize the lock. If it works it is done. If it doesn't work, the thread uses an atomic exchange to enqueue itself at the tail of the list of waiters.

There is not way to do a similar thing with circular buffers without locks or more complicated use of atomic instructions.