您的位置:首页 > 编程语言 > C#

C# ArrayList源码剖析

2016-04-19 15:29 603 查看
源代码版本为 .NET Framework 4.6.1

本系列持续更新,敬请关注

有投入,有产出。

数组是C#中最基础的一种数据类型,一旦初始化之后,容量便已经确定。若想要动态扩充容量,那么集合可以满足这点需求。ArrayList是C#最常用也是最基础的一个动态数组。

ArrayList在System.Collections中,实现了IList接口(IList:表示可按照索引进行访问的非泛型集合对象)。

如此来,ArrayList可以干点什么事情呢?

1. 可以动态扩容;

2. 插入/删除 比较便捷;

为了防止废话过多而导致翻船,上代码:

ArrayList中的数据是由一个内部数组来进行维护的,一个私有的Object数据(Object 你就是万能的造物主啊~)

private Object[] _item[];
private int _size;//ArrayList的容量
private const int _defaultCapacity = 4;//默认的容量
private static readonly Object[] emptyArray = EmptyArray<Object>.Value; //默认的空数组


- 添加元素:Add(Object) ,AddRange(ICollection),Insert(Int32, Object),InsertRange(Int32, ICollection)。

Add(Object):将对象添加到 ArrayList 的结尾处。

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++;
}
}


- 移除元素:Remove(Object),RemoveAt(Int32),RemoveRange(Int32, Int32),Clear()。

Remove(Object):从 ArrayList 中移除特定对象的第一个匹配项。在内部调用了IndexOf(Int32)和RemoveAt(Int32)。

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++;
}


- 排序:Sort(),Sort(IComparer),Sort(Int32, Int32, IComparer),Reverse(),Reverse(Int32, Int32)

Sort():将整个 ArrayList 中的元素进行排序。在ArrayList的排序方法全部在内部调用了Sort(Int32, Int32, IComparer),进行排序。

Sort(IComparer):使用指定的比较器对整个 ArrayList 中的元素进行排序。

Sort(Int32, Int32, IComparer):使用指定的比较器对 ArrayList 中某个范围内的元素进行排序。

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了,以使在某些情况下,可以避免内部数组的重复创建和数据位置的变换。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息