在 .Net BCL 中是否有类似于list的集合数据结构,它具有最大容量,例如配置为100个项目。当添加第101个项目时,将弹出/删除原始的第一个项目,从而确保项目数永远不会超过100。
我正在使用 .net 3.5
提前感谢
我正在使用 .net 3.5
提前感谢
目前没有这样的集合可用,但是编写一个相当容易。最好的方法是创建一个新的集合类型来封装现有的集合类型。
例如:
public class FixedSizeList<T> : IList<T> {
private List<T> _list = new List<T>();
private int _capacity = 100;
public void Add(T value) {
_list.Add(value);
while ( _list.Count > _capacity ) {
_list.RemoveAt(0);
}
}
// Rest omitted for brevity
}
public void Example<T>(IList<T> list, T value) {
var count = list.Count;
list.Add(value);
var addedValue = list[count];
}
对于指定的集合,唯一可以有效实现的集合接口是IEnumerable<T>
。我将我的实现作为示例留在那里。但是请查看ShuggyCoUk的答案,以获取一个IEnumerable<T>
的实现:
使用System;
使用System.Collections.Generic;
使用System.Text;
命名空间SharpNeat.Utility
{
///
/// 这是一个类型为T的通用循环缓冲区。必须在构造时分配循环缓冲区的容量。可以无限制地将项目加入队列,但是当缓冲区的容量达到时,缓冲区中最旧的值将被覆盖,因此最好将缓冲区视为循环数组或缓冲区。
///
public class CircularBuffer
{
///
/// 存储循环缓冲区值的内部数组。
///
protected T[] _buff;
///
/// 先前加入队列的项目的索引。如果缓冲区为空,则为-1。
///
protected int _headIdx;
///
/// 要出队的下一个项目的索引。如果缓冲区为空,则为-1。
///
protected int _tailIdx;
#region Constructors
///
/// 构造具有指定容量的循环缓冲区。
///
///
public CircularBuffer(int capacity)
{
_buff = new T[capacity];
_headIdx = _tailIdx = -1;
}
#endregion
#region Properties
///
/// 获取缓冲区中的项目数。如果已满,则返回缓冲区的容量。
///
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
///
/// 清除缓冲区。
///
public virtual void Clear()
{
_headIdx = _tailIdx = -1;
}
///
/// 将新项目加入队列。如果缓冲区已达到容量,则覆盖缓冲区中最旧的项目。
///
///
public virtual void Enqueue(T item)
{
if(_headIdx == -1)
{ // 缓冲区当前为空。
_headIdx = _tailIdx = 0;
_buff[0] = item;
return;
}
// 确定要写入的索引。
if(++_headIdx == _buff.Length)
{ // 围绕。
_headIdx = 0;
}
if(_headIdx == _tailIdx)
{ // 缓冲区溢出。递增tailIdx。
if(++_tailIdx == _buff.Length)
{ // 围绕。
_tailIdx=0;
}
_buff[_headIdx] = item;
return;
}
_buff[_headIdx] = item;
return;
}
///
/// 从缓冲区的后端删除最旧的项目并将其返回。
///
///
public virtual T Dequeue()
{
if(_tailIdx == -1)
{ // 缓冲区当前为空。
throw new InvalidOperationException("buffer is empty.");
}
T item = _buff[_tailIdx];
if(_tailIdx == _headIdx)
{ // 缓冲区现在为空。
_headIdx=_tailIdx=-1;
return item;
}
if(++_tailIdx == _buff.Length)
{ // 围绕。
_tailIdx = 0;
}
return item;
}
///
/// 从缓冲区的前端弹出最近添加的项目并将其返回。
///
///
public virtual T Pop()
{
if(_tailIdx == -1)
{ // 缓冲区当前为空。
throw new InvalidOperationException("buffer is empty.");
}
T item = _buff[_headIdx];
if(_
public class RollingWindow<T> : IEnumerable<T>
{
private readonly T[] data;
private int head;
private int nextInsert = 0;
public RollingWindow(int size)
{
if (size < 1)
throw new Exception();
this.data = new T[size];
this.head = -size;
}
public void Add(T t)
{
data[nextInsert] = t;
nextInsert = (nextInsert + 1) % data.Length;
if (head < 0)
head++;
}
public IEnumerator<T> GetEnumerator()
{
if (head < 0)
{
for (int i = 0; i < nextInsert; i++)
yield return data[i];
}
else
{
for(int i = 0; i < data.Length; i++)
yield return data[(nextInsert + i) % data.Length];
}
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
您也可以只覆盖 Collection
public class FixedSizeList<T> : Collection<T>
{
public int MaxItems {get;set;}
protected override void InsertItem(int index, T item){
base.InsertItem(index, item);
while (base.Count > MaxItems) {
base.RemoveItem(0);
}
}
}
目前没有现成的方法,但是可以很容易地通过数组或集合编写一个函数来实现此功能。
public class CircularBuffer<T> : ICollection<T>, IEnumerable<T>, ICollection, IEnumerable
{
private int capacity;
private int size;
private int head;
private int tail;
private T[] buffer;
[NonSerialized()]
private object syncRoot;
public CircularBuffer(int capacity)
: this(capacity, false)
{
}
public CircularBuffer(int capacity, bool allowOverflow)
{
if (capacity < 0)
throw new ArgumentException(Properties.Resources.MessageZeroCapacity, "capacity");
this.capacity = capacity;
size = 0;
head = 0;
tail = 0;
buffer = new T[capacity];
AllowOverflow = allowOverflow;
}
public bool AllowOverflow
{
get;
set;
}
public int Capacity
{
get { return capacity; }
set
{
if (value == capacity)
return;
if (value < size)
throw new ArgumentOutOfRangeException("value", Properties.Resources.MessageCapacityTooSmall);
var dst = new T[value];
if (size > 0)
CopyTo(dst);
buffer = dst;
capacity = value;
}
}
public int Size
{
get { return size; }
}
public bool Contains(T item)
{
int bufferIndex = head;
var comparer = EqualityComparer<T>.Default;
for (int i = 0; i < size; i++, bufferIndex++)
{
if (bufferIndex == capacity)
bufferIndex = 0;
if (item == null && buffer[bufferIndex] == null)
return true;
else if ((buffer[bufferIndex] != null) &&
comparer.Equals(buffer[bufferIndex], item))
return true;
}
return false;
}
public void Clear()
{
size = 0;
head = 0;
tail = 0;
}
public int Put(T[] src)
{
return Put(src, 0, src.Length);
}
public int Put(T[] src, int offset, int count)
{
if (!AllowOverflow && count > capacity - size)
throw new InvalidOperationException(Properties.Resources.MessageBufferOverflow);
int srcIndex = offset;
for (int i = 0; i < count; i++, tail++, srcIndex++)
{
if (tail == capacity)
tail = 0;
buffer[tail] = src[srcIndex];
}
size = Math.Min(size + count, capacity);
return count;
}
public void Put(T item)
{
if (!AllowOverflow && size == capacity)
throw new InvalidOperationException(Properties.Resources.MessageBufferOverflow);
buffer[tail] = item;
if (++tail == capacity)
tail = 0;
size++;
}
public void Skip(int count)
{
head += count;
if (head >= capacity)
head -= capacity;
}
public T[] Get(int count)
{
var dst = new T[count];
Get(dst);
return dst;
}
public int Get(T[] dst)
{
return Get(dst, 0, dst.Length);
}
public int Get(T[] dst, int offset, int count)
{
int realCount = Math.Min(count, size);
int dstIndex = offset;
for (int i = 0; i < realCount; i++, head++, dstIndex++)
{
if (head == capacity)
head = 0;
dst[dstIndex] = buffer[head];
}
size -= realCount;
return realCount;
}
public T Get()
{
if (size == 0)
throw new InvalidOperationException(Properties.Resources.MessageBufferEmpty);
var item = buffer[head];
if (++head == capacity)
head = 0;
size--;
return item;
}
public void CopyTo(T[] array)
{
CopyTo(array, 0);
}
public void CopyTo(T[] array, int arrayIndex)
{
CopyTo(0, array, arrayIndex, size);
}
public void CopyTo(int index, T[] array, int arrayIndex, int count)
{
if (count > size)
throw new ArgumentOutOfRangeException("count", Properties.Resources.MessageReadCountTooLarge);
int bufferIndex = head;
for (int i = 0; i < count; i++, bufferIndex++, arrayIndex++)
{
if (bufferIndex == capacity)
bufferIndex = 0;
array[arrayIndex] = buffer[bufferIndex];
}
}
public IEnumerator<T> GetEnumerator()
{
int bufferIndex = head;
for (int i = 0; i < size; i++, bufferIndex++)
{
if (bufferIndex == capacity)
bufferIndex = 0;
yield return buffer[bufferIndex];
}
}
public T[] GetBuffer()
{
return buffer;
}
public T[] ToArray()
{
var dst = new T[size];
CopyTo(dst);
return dst;
}
#region ICollection<T> Members
int ICollection<T>.Count
{
get { return Size; }
}
bool ICollection<T>.IsReadOnly
{
get { return false; }
}
void ICollection<T>.Add(T item)
{
Put(item);
}
bool ICollection<T>.Remove(T item)
{
if (size == 0)
return false;
Get();
return true;
}
#endregion
#region IEnumerable<T> Members
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region ICollection Members
int ICollection.Count
{
get { return Size; }
}
bool ICollection.IsSynchronized
{
get { return false; }
}
object ICollection.SyncRoot
{
get
{
if (syncRoot == null)
Interlocked.CompareExchange(ref syncRoot, new object(), null);
return syncRoot;
}
}
void ICollection.CopyTo(Array array, int arrayIndex)
{
CopyTo((T[])array, arrayIndex);
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)GetEnumerator();
}
#endregion
}
您可以在此处找到循环缓冲区的实现:http://circularbuffer.codeplex.com
IList<T>
而不是继承自具体类型的清晰解释真是太好了。 - Andrew HareIList<T>
的语义为什么不适合这个实现?IList<T>
没有任何限制实现根据其中定义的任何操作更改项目索引。当您调用任一删除方法时,您认为会发生什么?请您思考一下。 - Andrew Hare