Reputation: 6353
I need a C# data structure that works in the following way:
Example: If I define a structure of 5 elements and added the following
1,2,3,4,5,6,7,8
The data structure would look like the following:
4,5,6,7,8
I'm not sure which structure will work in this way. Vector? List? Stack? The data structure supports a static size like an array and push data that pushes off old data.
Stack/queue doesn't provide random access. List doesn't have a "push" operation.
Perhaps a LinkedList and add custom operation for "push" that removes the first element? LinkList random access is o(n) operation though.
Upvotes: 2
Views: 553
Reputation: 1
The data structure you are talking about i circular buffer (or ring buffer). In C#, you can implement this using an array. Sample implementation-
public class CircularBuffer<T>
{
private T[] buffer;
private int head;
private int tail;
private int size;
private int count;
public CircularBuffer(int size)
{
this.size = size;
buffer = new T[size];
head = 0;
tail = 0;
count = 0;
}
public void Add(T item)
{
buffer[tail] = item;
tail = (tail + 1) % size;
if (count == size)
{
head = (head + 1) % size;
}
else
{
count++;
}
}
public T Get(int index)
{
if (index < 0 || index >= count)
{
throw new IndexOutOfRangeException();
}
return buffer[(head + index) % size];
}
public int Count
{
get { return count; }
}
}
Upvotes: 0
Reputation: 3022
Queue is the best in this situation, You should write your own wrapper class that check your count(limit) before Enqueue(Adding new data to end of list) to make it behave like a fixed one, here is an example:
public class FixedQueue<T> : Queue<T>
{
//to sets the total number of elements that can be carried without resizing,
//we called the base constrctor that takes the capacity
private Random random;
public int Size { get; private set; }
public FixedQueue(int size, Random random)
:base(size)
{
this.Size = size;
this.random = random;
}
public new void Enqueue(T element)
{
base.Enqueue(element);
lock (this)
while (base.Count > Size)
base.Dequeue(); //as you said, "Oldest data falls off" :)
}
public T RandomAcess()
{
return this.ElementAt(random.Next(Count));
}
}
Upvotes: 0
Reputation: 881403
For maximum efficiency, that would probably be a custom class implementing a circular buffer.
Just have a fixed size array created at instantiation time to hold the data. In addition have a start index, a size member and a capacity so you know how much data is in the buffer and where it starts.
So, to start with, your list contains no data, the start position is 0 and the size is 0.
When you add an item, it goes into element (start + size) % capacity
and size
is incremented if it's not yet at capacity
. If it was at capacity
, you increment start
as well, wrapping around if need be: start = (start + 1) % capacity
.
To get an element at index n
from the list, you actually adjust it with start
:
return element[(start + n) % capacity];
I haven't covered removing the start of the list since that's not in your specs. However, it's a simple check to ensure size
is not 0, then extracting the item at element[start]
, then incrementing start
with the same wraparound shown above.
In pseudo-code (untested but should be close):
def listNew (capacity):
me = new object
me.capacity = capacity
me.data = new array[capacity]
me.start = 0
me.size = 0
return me
def listAdd (me, item):
if me.size = me.capacity:
me.data[me.start] = item
me.start = (me.start + 1) % me.capacity
else:
me.data[(me.start + me.size) % me.capacity] = item
me.size = me.size + 1
def listGet (me, index):
if index > size:
return 0 # or raise error of some sort
return me.data[(me.start + index) % me.capacity]
def listRemove (me):
if size == 0:
return 0 # or raise error of some sort
item = me.data[(me.start + index) % me.capacity]
me.start = (me.start + 1) % me.capacity
me.size = me.size - 1
return item
All of these operations are O(1) time complexity, as requested.
For your particular example of adding the numbers 1
through 8
to a five-element list, you would end up with:
0 1 2 3 4 <--- index
+---+---+---+---+---+
| 6 | 7 | 8 | 4 | 5 |
+---+---+---+---+---+
^
+--------- start = 3
size = 5
capacity = 5
That way, extracting virtual index 3 (the fourth number) from the buffer would give you an actual index of:
(start + 3) % capacity
= ( 3 + 3) % 5
= 6 % 5
= 1
Upvotes: 5
Reputation: 1325
It's a maximum-length queue (such that you must de-queue and discard one element before queuing another once it reaches the maximum length). You can do random access on a C# Queue but it's O(n) (using ElementAt LINQ extension), which presumably isn't really an issue if 5 is a typical size. If you want O(1) I suspect you'll have to roll your own (https://github.com/mono/mono/blob/master/mcs/class/System/System.Collections.Generic/Queue.cs?source=cc might help!)
Upvotes: 2