Click here to Skip to main content
15,887,746 members
Articles / Programming Languages / C#
Article

A Generic Circular Array

Rate me:
Please Sign up or sign in to vote.
3.21/5 (10 votes)
10 Dec 2008CPOL2 min read 66.1K   799   26   15
An efficient circular array - fixed length first in, last out.

Introduction

CircularArray<T> is a class that implements a fixed length first in last out 'queue' or buffer. This is useful, for example, if you want to keep, say, the last 30 values in a real-time system. The next value gets put into the array, and then the last value in the array gets pushed out. It does this by using a fixed array, and uses the DivRem math operator to calculate the position of the head and the tail in the array. It's like the array is circular, with a pointer moving around the circle. The only downside is I had to use an internal facade array to get an array in the sequence I wanted, copying out the values; obviously, this isn't great. I can't think of anything else unless I decide to implement something that appears as an array but is actually a linked list or something.

Background

I needed to keep the last X values in a real-time stock tracking application, efficiently, to work out various things. I couldn't find anything like this at all in Generics (I might be wrong. If I am, can someone please tell me about the built-in class available for this?).

Using the code

The constructor specifies the number of elements to hold. Here, I am holding 'Bar' objects:

C#
CircularArray<Bar> _circularArray = new CircularArray<Bar>(periods);

Push a value onto the array using the Push method:

C#
_circularArray.Push(value);

The Get method gets the element X from the head, e.g., Get(0) gets the last value you pushed on, Get(1) gets the second last value you pushed on etc.

C#
_circularArray.Get(i)

The Array method returns an array sequenced from tail to head, e.g., from the oldest value to the newest values that were pushed on to the queue. One use of this may be to use this as input into a TA-LIB technical analysis library, or anything where you will need an array of the last X values in a real-time system. Here is a snippet of the Unit Test of Array.

C#
[Test]

public void Push()
{

    CircularArray<int> cq = new CircularArray<int>(5);

    cq.Push(1);
    cq.Push(2);
    cq.Push(3);
    cq.Push(4);
    cq.Push(5);
    cq.Push(6);
    cq.Push(7);
    cq.Push(8);
    cq.Push(9);
    cq.Push(10);
    cq.Push(11);

    Assert.AreEqual(7, cq.Array[0]);
    Assert.AreEqual(8, cq.Array[1]);
    Assert.AreEqual(9, cq.Array[2]);
    Assert.AreEqual(10, cq.Array[3]);
    Assert.AreEqual(11, cq.Array[4]);

}

I've included the code and Unit Tests in the solution Zip file; everything is there.

Points of interest

As mentioned, I couldn't find anything like this already available. I've probably missed out on some sort of built-in generic Microsoft class. If anyone knows about such a thing, please let me know!

Update: Note, after I posted this, I did read Marc Clifton's article 'A Circular List'. After plugging it into an NUnit test equivalent to the one above:

C#
[Test]
public void CicularList()
{

    CircularList<int> cq = new CircularList<int>(5);

    cq.Value = 1;
    cq.Next();

    cq.Value = 2;
    cq.Next();

    cq.Value = 3;
    cq.Next();

    cq.Value = 4;
    cq.Next();

    cq.Value = 5;
    cq.Next();

    cq.Value = 6;
    cq.Next();

    cq.Value = 7;
    cq.Next();

    cq.Value = 8;
    cq.Next();

    cq.Value = 9;
    cq.Next();

    cq.Value = 10;
    cq.Next();

    cq.Value = 11;
    cq.Next();

    Assert.AreEqual(7, cq[0]); // Failed its 11
    Assert.AreEqual(8, cq[1]); // Failed its 7
    Assert.AreEqual(9, cq[2]); // Failed its 8
    Assert.AreEqual(10, cq[3]); // Failed its 9
    Assert.AreEqual(11, cq[4]); // Failed its 10

}

The array cq would be 11,7,8,9,10, which is the raw circular array. I was expecting 7,8,9,10,11 - e.g., the last 5 values, oldest to newest. Maybe, I'm using it wrong. 'Failed' above is just against what I expected, so don't read into it. I'm sure CircularList is useful for some, just not for my purposes. I totally respect and value Marc Clifton's contributions.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
United Kingdom United Kingdom
Jean-marc is a IT consultant specializing on the microsoft platform. Jean-marc lives in London, UK.

Comments and Discussions

 
Generalcircular buffers with contiguous storage of values Pin
suralis15-Dec-08 23:40
suralis15-Dec-08 23:40 
Hi,

Nice article. I've mentioned a problem: "The only downside is I had to use an internal facade array to get an array in the sequence I wanted, copying out the values; obviously, this isn't great.".

Yes, it can be a problem with long enough circular buffers if one needs to access the content of the buffer at certain times as creating a new long array and copying lots of values to it is rather costly.

One possible solution is double sized buffers. It is a C-style trick used for example in implementation of FIR filters etc. The idea is simple. Just create an array that is two times longer than the maximum length of circular buffer. When adding a new value, add it into two places i.e. once at the index of new value and the second time at the (index + max_value). When you need to read the contents of the buffers starting from a specific index in the past you just take the array slice starting from the index in the first half of the double-sized array. Then the hole sequence of elements can be accessed in contiguous way. Part of it is taken from the first half and the rest from the second half. Sorry if the explanation is not clear enough.

Say, if index_ contains the index of the value being added:

<pre>
value_type buffer[2*BUFFER_SIZE];
int index = 0;
void add_value( value_type value_to_be_added )
{
buffer[index] = buffer[index+BUFFER_SIZE] = value_to_be_added;
++index;
index %= MAX_VALUE;
}

value_type* get_contiguous_buffer_( int starting_from )
{
return buffer + index + starting_from;
}
</pre>

Sure, it's C/C++. But I hope that it can be read by a C# programmer and translated into C# with little effort.

If the buffers is "long enough", the cheap access to contiguous memory compensates for more expensive addition of each element. On the other hand, if the buffer is short, it may be more efficient just to create a new array and copy values to it.

Concurrent access to the buffer may be problematic as well. From personal experience, one usually needs kind of buffer snapshot to be passed to a background thread from time to time. It normally requires copying buffer contents anyway. Copying contiguous memory can be so much cheaper in terms of performance that this may well compensate for additional cost of two assignments while adding values.

So it's my two cents Big Grin | :-D
GeneralRe: circular buffers with contiguous storage of values Pin
Jean-marc Lai17-Dec-08 10:50
Jean-marc Lai17-Dec-08 10:50 
GeneralThoughts Pin
PIEBALDconsult10-Dec-08 9:29
mvePIEBALDconsult10-Dec-08 9:29 
GeneralRe: Thoughts Pin
Jean-marc Lai10-Dec-08 23:02
Jean-marc Lai10-Dec-08 23:02 
GeneralRe: Thoughts [modified] Pin
PIEBALDconsult11-Dec-08 8:44
mvePIEBALDconsult11-Dec-08 8:44 
GeneralRe: Thoughts Pin
Jean-marc Lai12-Dec-08 15:20
Jean-marc Lai12-Dec-08 15:20 
GeneralRe: Thoughts Pin
PIEBALDconsult12-Dec-08 18:11
mvePIEBALDconsult12-Dec-08 18:11 
General_facadeArray problems Pin
Luc Pattyn10-Dec-08 4:37
sitebuilderLuc Pattyn10-Dec-08 4:37 
GeneralRe: _facadeArray problems Pin
Jean-marc Lai10-Dec-08 5:12
Jean-marc Lai10-Dec-08 5:12 
GeneralRe: _facadeArray problems Pin
Luc Pattyn10-Dec-08 5:51
sitebuilderLuc Pattyn10-Dec-08 5:51 
GeneralRe: _facadeArray problems Pin
Jean-marc Lai10-Dec-08 23:06
Jean-marc Lai10-Dec-08 23:06 
GeneralRe: _facadeArray problems Pin
Luc Pattyn11-Dec-08 0:16
sitebuilderLuc Pattyn11-Dec-08 0:16 
GeneralRe: _facadeArray problems Pin
Jean-marc Lai11-Dec-08 0:35
Jean-marc Lai11-Dec-08 0:35 
GeneralRe: _facadeArray problems Pin
Luc Pattyn11-Dec-08 1:36
sitebuilderLuc Pattyn11-Dec-08 1:36 
GeneralGood Article Pin
Donsw10-Dec-08 4:31
Donsw10-Dec-08 4:31 

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.