Maximum capacity collection in c#

.net, c#

Solution

What you are describing is a circular buffer. I use these occasionally and recently ported some older code into a genericised C# class (attached). This code is part of SharpNeat V2 development.

This has O(1) performance on add and remove oprations whereas the solution posted that encapsulates a List is O(n). This is because removing the 0th item in a list causes all other items to be shuffled down to fill the gap.

using System;
using System.Collections.Generic;
using System.Text;

namespace SharpNeat.Utility
{
    /// 
    /// This is a generic circular buffer of items of type T.  A circular buffer must be assigned
    /// a capacity at construction time. Items can be enqueued indefintely, but when the buffer's 
    /// capacity is reached the oldest values in the buffer are overwritten, thus the buffer is best
    /// thought of as a circular array or buffer.
    /// 
    public class CircularBuffer
    {
        /// 
        /// Internal array that stores the circular buffer's values.
        /// 
        protected T[] _buff;

        /// 
        /// The index of the previously enqueued item. -1 if buffer is empty.
        /// 
        protected int _headIdx;

        /// 
        /// The index of the next item to be dequeued. -1 if buffer is empty.
        /// 
        protected int _tailIdx;

        #region Constructors

        /// 
        /// Constructs a circular buffer with the specified capacity.
        /// 
        /// 
        public CircularBuffer(int capacity)
        {
            _buff = new T[capacity];
            _headIdx = _tailIdx = -1;
        }

        #endregion

        #region Properties

        /// 
        /// Gets the number of items in the buffer. Returns the buffer's capacity
        /// if it is full.
        /// 
        public int Length
        {
            get
            {
                if(_headIdx == -1) 
                    return 0;

                if(_headIdx > _tailIdx)
                    return (_headIdx - _tailIdx) + 1;

                if(_tailIdx > _headIdx)
                    return (_buff.Length - _tailIdx) + _headIdx + 1;

                return 1;
            }
        }

        #endregion

        #region Public Methods

        /// 
        /// Clear the buffer.
        /// 
        public virtual void Clear()
        {
            _headIdx = _tailIdx = -1;
        }

        /// 
        /// Enqueue a new item. This overwrites the oldest item in the buffer if the buffer
        /// has reached capacity.
        /// 
        /// 
        public virtual void Enqueue(T item)
        {
            if(_headIdx == -1)
            {   // buffer is currently empty.
                _headIdx = _tailIdx = 0;
                _buff[0] = item;
                return;
            }

            // Determine the index to write to.
            if(++_headIdx == _buff.Length)
            {   // Wrap around.
                _headIdx = 0;
            }

            if(_headIdx == _tailIdx)
            {   // Buffer overflow. Increment tailIdx.
                if(++_tailIdx == _buff.Length) 
                {   // Wrap around.
                    _tailIdx=0;
                }
                _buff[_headIdx] = item;
                return;
            }

            _buff[_headIdx] = item;
            return;
        }

        /// 
        /// Remove the oldest item from the back end of the buffer and return it.
        /// 
        /// 
        public virtual T Dequeue()
        {
            if(_tailIdx == -1)
            {   // buffer is currently empty.
                throw new InvalidOperationException("buffer is empty.");
            }

            T item = _buff[_tailIdx];

            if(_tailIdx == _headIdx)
            {   // The buffer is now empty.
                _headIdx=_tailIdx=-1;
                return item;
            }

            if(++_tailIdx == _buff.Length)
            {   // Wrap around.
                _tailIdx = 0;
            }

            return item;
        }

        /// 
        /// Pop the most recently added item from the front end of the buffer and return it.
        /// 
        /// 
        public virtual T Pop()
        {
            if(_tailIdx == -1)
            {   // buffer is currently empty.
                throw new InvalidOperationException("buffer is empty.");
            }   

            T item = _buff[_headIdx];

            if(_tailIdx == _headIdx)
            {   // The buffer is now empty.
                _headIdx = _tailIdx =- 1;
                return item;
            }

            if(--_headIdx==-1)
            {   // Wrap around.
                _headIdx=_buff.Length-1;
            }

            return item;
        }

        #endregion
    }
}

Problem

In the .Net BCL is there a collection data structure similar to list that has a maximum capacity, say configured to 100 items, which when item 101 is added the original first item is popped/removed from the collection thus ensuring the item count never exceeds 100. I’m using .net 3.5 Thanks in advance

Original source

Related problems