我还没有找到任何好的实现。所以我写了我自己的 ChunkyList。通常 ChunkyList 是数组包装器列表。最初块的大小为 1,但每次需要扩展当前块时,它乘以 2(当它达到 MaxBlockSize 时,会创建下一个块)(行为类似于 List)。
这是一个通用的 ChunkyList:
public class ChunkyList<T> : IList<T>
{
public ChunkyList()
{
MaxBlockSize = 65536;
}
public ChunkyList(int maxBlockSize)
{
MaxBlockSize = maxBlockSize;
}
private List<T[]> _blocks = new List<T[]>();
public int Count { get; private set; }
public int MaxBlockSize { get; private set; }
public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}
public IEnumerator<T> GetEnumerator()
{
var index = 0;
foreach (var items in _blocks)
foreach (var item in items)
{
yield return item;
if (Count <= ++index)
break;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(T item)
{
var indexInsideBlock = GetIndexInsideBlock(Count);
if (indexInsideBlock == 0)
_blocks.Add(new T[1]);
else
{
var lastBlockIndex = _blocks.Count - 1;
var lastBlock = _blocks[lastBlockIndex];
if(indexInsideBlock >= lastBlock.Length)
{
var newBlockSize = lastBlock.Length*2;
if (newBlockSize >= MaxBlockSize)
newBlockSize = MaxBlockSize;
_blocks[lastBlockIndex] = new T[newBlockSize];
Array.Copy(lastBlock, _blocks[lastBlockIndex], lastBlock.Length);
}
}
_blocks[GetBlockIndex(Count)][indexInsideBlock] = item;
Count++;
}
public void AddRange(IEnumerable<T> items)
{
foreach (var item in items)
Add(item);
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(T item)
{
throw new NotImplementedException();
}
public void CopyTo(T[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public bool Remove(T item)
{
throw new NotImplementedException();
}
public int IndexOf(T item)
{
throw new NotImplementedException();
}
public void Insert(int index, T item)
{
throw new NotImplementedException();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
public T this[int index]
{
get
{
if (index >= Count)
throw new ArgumentOutOfRangeException("index");
var blockIndex = GetBlockIndex(index);
var block = _blocks[blockIndex];
return block[GetIndexInsideBlock(index)];
}
set { throw new NotImplementedException(); }
}
private int GetBlockIndex(int index)
{
return index / MaxBlockSize;
}
private long GetIndexInsideBlock(int index)
{
return index % MaxBlockSize;
}
}
以下是证明此实现有效的测试:
[TestClass]
public class ChunkyListTests
{
[TestMethod]
public void GetEnumerator_NoItems()
{
var chunkyList = new ChunkyList<float>();
var wasInsideForeach = false;
foreach (var item in chunkyList)
wasInsideForeach = true;
Assert.IsFalse(wasInsideForeach);
}
[TestMethod]
public void GetEnumerator_MaxBlockSizeOfThreeWithThreeItems()
{
var chunkyList = new ChunkyList<float> (3) { 1, 2, 3 };
var wasInsideForeach = false;
var iteratedItems = new List<float>();
foreach (var item in chunkyList)
{
wasInsideForeach = true;
iteratedItems.Add(item);
}
Assert.IsTrue(wasInsideForeach);
CollectionAssert.AreEqual(new List<float> { 1, 2, 3 }, iteratedItems);
}
[TestMethod]
public void GetEnumerator_MaxBlockSizeOfTwoWithThreeItems()
{
var chunkyList = new ChunkyList<float>(2) {1,2,3};
var wasInsideForeach = false;
var iteratedItems = new List<float>();
foreach (var item in chunkyList)
{
wasInsideForeach = true;
iteratedItems.Add(item);
}
Assert.IsTrue(wasInsideForeach);
CollectionAssert.AreEqual(new List<float>() { 1, 2, 3 }, iteratedItems);
Assert.AreEqual(chunkyList.MaxBlockSize, 2);
}
}
PS 我只实现了我的代码中使用的那些 IList 方法。因此,欢迎您改进此实现。