Click here to Skip to main content
15,885,985 members
Articles / Programming Languages / C#
Tip/Trick

A Generic Circular Buffer in C#

Rate me:
Please Sign up or sign in to vote.
5.00/5 (10 votes)
6 Feb 2020MIT1 min read 36.9K   1K   9   11
A circular buffer implementing IList

CircularDemo

Introduction

Microsoft .NET provides a few basic generic data structures such as Stack<T>, Queue<T>, and LinkedList<T>, but no circular buffer or DEque ("double ended" queue). This class aims to fill that gap.

Conceptualizing this Mess

A circular buffer allows for rapid pushing and popping from the front and back of the container. These operations are O(1) while other inserts and removes are O(n) assuming no reallocations.

This makes the buffer suitable as a generic stack or queue. The reason one might want to use this class this way despite Microsoft's stack or queue is that this class allows access by index, and fully implements IList<T>

Using this Mess

The class is relatively easy to use. Most of the IList<T> and ICollection<T> interface members are explicit because most of their operations are O(n) instead of O(1). This means you'll have to cast to the appropriate interface in order to fully access its list or collection members. This is to prevent casual misuse of the data structure - it's not intended primarily as a list class, but may be used as one. The access modifiers reflect this.

The primary API consists of PushBack()/PopBack() and PushFront()/PopFront() which add and remove items from the back or front of the container, respectively. There are also some more standard list/collection members like Contains(), IndexOf(), this[] and Clear()

Here's an excerpt from the demo/test code:

C#
Console.WriteLine("Adding 10 items");
for (var i = 0; i < 10; ++i)
    list.PushBack(i + 1);

Console.Write("Enumerating "+ list.Count+" items:");
foreach (var item in list)
    Console.Write(" " + item.ToString());
Console.WriteLine();

Console.WriteLine("Removing 1 item");
list.PopFront();

It's all doc commented and straightforward.

Points of Interest

I really can't stand implementing Insert(), especially over a circular buffer, and if there's a bug, it's probably in the Insert() code. I'm not sure if the routine can be simplified. There are a lot of corner cases.

History

  • 6th February, 2020 - Initial submission

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
United States United States
Just a shiny lil monster. Casts spells in C++. Mostly harmless.

Comments and Discussions

 
QuestionPushFront behavior Pin
Kermel11-Mar-21 12:15
Kermel11-Mar-21 12:15 
After some testing I have realized one more strange behavior. Consider this:

C#
var list = new CircularBuffer<int>(5);
            for(var i = 0; i < 10; i++)
            {
                list.PushFront(i);
            }
            Console.Write("Enumerating " + list.Count + " items:");
            foreach (var item in list)
                Console.Write(" " + item.ToString());


The result is this:
Quote:
Enumerating 10 items: 9 8 7 6 5 9 8 7 6 5


However internaly the size of the buffer doesn't change, it is still an array of 5 items but the count counter is increasing with every PushFront call. So the final enumeration actually enumerates everything twice.

I like the idea of non-growing buffer in case of using the PushFront way of inserting items (this is exactly what I have been looking for for my usage) and I suggest the following fix to be considered:

C#
public void PushFront(T item)
        {
            --_start;
            if (0 > _start)
				_start += _items.Length;
			_items[_start] = item;
            if (_count < _items.Length)
            {
                ++_count;
            }
            unchecked { ++_version; }
		}

(i.e. raising the _count only till the capacity of the buffer)
QuestionIndexer access behavior Pin
Kermel11-Mar-21 11:44
Kermel11-Mar-21 11:44 
QuestionPopBack bug Pin
Kermel11-Mar-21 11:23
Kermel11-Mar-21 11:23 
QuestionThis is DList<T> Pin
Qwertie16-Feb-20 5:24
Qwertie16-Feb-20 5:24 
QuestionActually a Circular Buffer? Pin
Bruce Greene8-Feb-20 5:26
Bruce Greene8-Feb-20 5:26 
AnswerRe: Actually a Circular Buffer? Pin
honey the codewitch8-Feb-20 5:50
mvahoney the codewitch8-Feb-20 5:50 
GeneralRe: Actually a Circular Buffer? Pin
Bruce Greene8-Feb-20 6:37
Bruce Greene8-Feb-20 6:37 
GeneralRe: Actually a Circular Buffer? Pin
honey the codewitch8-Feb-20 7:24
mvahoney the codewitch8-Feb-20 7:24 
QuestionCircularBuffer Pin
Howard Duane Allman7-Feb-20 9:55
professionalHoward Duane Allman7-Feb-20 9:55 
AnswerRe: CircularBuffer Pin
honey the codewitch7-Feb-20 11:08
mvahoney the codewitch7-Feb-20 11:08 
AnswerRe: CircularBuffer Pin
Member 116912058-Feb-20 3:13
Member 116912058-Feb-20 3:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.