C# ArrayList源码剖析

源代码版本为 .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.Value; //默认的空数组

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

  1. 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()。

  1. 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)

  1. Sort():将整个 ArrayList 中的元素进行排序。在ArrayList的排序方法全部在内部调用了Sort(Int32, Int32, IComparer),进行排序。
  2. Sort(IComparer):使用指定的比较器对整个 ArrayList 中的元素进行排序。
  3. 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了,以使在某些情况下,可以避免内部数组的重复创建和数据位置的变换。

你可能感兴趣的:(.NET数据结构系列)