源代码版本为 .NET Framework 4.6.1
有投入,有产出。
数组是C#中最基础的一种数据类型,一旦初始化之后,容量便已经确定。若想要动态扩充容量,那么集合可以满足这点需求。ArrayList是C#最常用也是最基础的一个动态数组。
ArrayList在System.Collections中,实现了IList接口(IList:表示可按照索引进行访问的非泛型集合对象)。
如此来,ArrayList可以干点什么事情呢?
1. 可以动态扩容;
2. 插入/删除 比较便捷;
为了防止废话过多而导致翻船,上代码:
private Object[] _item[];
private int _size;//ArrayList的容量
private const int _defaultCapacity = 4;//默认的容量
private static readonly Object[] emptyArray = EmptyArray<Object>.Value; //默认的空数组
public virtual int Add(Object value) {
Contract.Ensures(Contract.Result<int>() >= 0);//后置契约,返回值必须大于等于0
//动态扩容的核心方法,当ArrayList的容量等于内部存放数据的数组长度时,进行扩容
if (_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size] = value;//在ArrayList末尾索引处写入值
_version++;
return _size++;//返回数组ArrayList的容量
}
//动态扩容的核心方法,一次扩容长度为初始容量的2倍,容量不得超出long的最大范围
private void EnsureCapacity(int min) {
if (_items.Length < min) {
int newCapacity = _items.Length == 0
? _defaultCapacity: _items.Length * 2;
//Array.MaxArrayLength为long的最大值
if ((uint)newCapacity > Array.MaxArrayLength)
newCapacity = Array.MaxArrayLength;
if (newCapacity < min) newCapacity = min;
Capacity = newCapacity;
}
}
//自动扩容的实现原理是 创建一个更大容量新的组数,将原数组的数据搬至新组数
public virtual int Capacity {
get {
Contract.Ensures(Contract.Result<int>() >= Count);
return _items.Length;
}
set {
if (value < _size) {
throw new ArgumentOutOfRangeException("value",
Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
}
Contract.Ensures(Capacity >= 0);
Contract.EndContractBlock();
if (value != _items.Length) {
if (value > 0) {
Object[] newItems = new Object[value];
if (_size > 0) {
Array.Copy(_items, 0, newItems, 0, _size);
}
_items = newItems;
}
else {
_items = new Object[_defaultCapacity];
}
}
}
}
2. AddRange(ICollection):添加 ICollection 的元素到 ArrayList 的末尾。该方法在内部调用了InsertRange(Int32, ICollection)方法
public virtual void AddRange(ICollection c) {
InsertRange(_size, c);
}
3. Insert(Int32, Object) :将元素插入 ArrayList 的指定索引处。
public virtual void Insert(int index, Object value) {
if(index < 0 || index > _size)
throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_ArrayListInsert"));
Contract.EndContractBlock();
//扩容检查
if(_size == _items.Length) EnsureCapacity(_size + 1);
//插入到指定索引处,可以看出,由于需要移动元素的位置,相对于Add直接插入到末尾来说,效率慢了点
if(index < _size){
Array.Copy(_items, index, _items, index + 1, _size - index);
}
_items[index] = value;
_size++;
_version++;
}
4. InsertRange(Int32, ICollection):将ICollection中的元素插入到 ArrayList 中指定索引处。
public virtual void InsertRange(int index, ICollection c) {
if (c==null)
throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
if (index < 0 || index > _size)
throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
Contract.EndContractBlock();//契约结尾
int count = c.Count;//即将要添加到ArrayList的元素数量
if (count > 0) {
//每次添加元素,都会进行扩容判断
EnsureCapacity(_size + count);
//友谊的小船说翻就翻,看看微软大神在功能与性能上所做的取舍吧
if (index < _size) {
//移动元素位置
Array.Copy(_items, index, _items, index + count, _size - index);
}
//后面这段没有用for循环,也许是微软大神为了代码的重用吧?
//创建新数组用于存储即将插入ArrayList的元素
Object[] itemsToInsert = new Object[count];
c.CopyTo(itemsToInsert, 0);
//将元素插入对应的索引处
itemsToInsert.CopyTo(_items, index);
_size += count;
_version++;
}
}
public virtual void Remove(Object obj) {
Contract.Ensures(Count >= 0);
//调用IndexOf(Int32)获取元素的索引
int index = IndexOf(obj);
if(index >= 0)
RemoveAt(index);
}
2.RemoveAt(Int32):移除 ArrayList 的指定索引处的元素。
public virtual void RemoveAt(int index) {
if (index < 0 || index >= _size)
throw new ArgumentOutOfRangeException("index",
Environment.GetResourceString("ArgumentOutOfRange_Index"));
Contract.Ensures(Count >= 0);
Contract.EndContractBlock();
_size--;//移除元素,容量自减1
//移除元素也会导致元素位置的变动,导致效率降低
if (index < _size) {
Array.Copy(_items, index + 1, _items, index, _size - index);
}
_items[_size] = null;
_version++;
}
3.RemoveRange(Int32, Int32):从 ArrayList 中移除一定范围的元素。
public virtual void Clear() {
if (_size > 0)
{
//快刀斩乱麻
Array.Clear(_items, 0, _size);
_size = 0;
}
_version++;
}
public virtual void Sort(int index, int count, IComparer comparer) {
if (index < 0)
throw new ArgumentOutOfRangeException("index",
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (count < 0)
throw new ArgumentOutOfRangeException("count",
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (_size - index < count)
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
Contract.EndContractBlock();
//使用了基础数组排序
Array.Sort(_items, index, count, comparer);
_version++;
}
4.Reverse():将整个 ArrayList 中元素的顺序反转。
public virtual void Reverse(int index, int count) {
if (index < 0)
throw new ArgumentOutOfRangeException("index",
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (count < 0)
throw new ArgumentOutOfRangeException("count",
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (_size - index < count)
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
Contract.EndContractBlock();
Array.Reverse(_items, index, count);
_version++;
}
5.Reverse(Int32, Int32):将指定范围中元素的顺序反转。
public virtual void Reverse(int index, int count) {
if (index < 0)
throw new ArgumentOutOfRangeException("index",
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (count < 0)
throw new ArgumentOutOfRangeException("count",
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (_size - index < count)
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
Contract.EndContractBlock();
Array.Reverse(_items, index, count);
_version++;
}
ArrayList可以说是C#中最简单的一种数据结构了,再使用ArrayList时为了获得一些性能上的提升,尽量在可预知的容量大小范围内使用一个固定的初始容量来构建ArrayList了,以使在某些情况下,可以避免内部数组的重复创建和数据位置的变换。