C#List<T>源码详解

【源码地址】

https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,cf7f4095e4de7646

【实现原理】

将泛型数据(对值类型来说就是数据本身,对引用类型来说就是引用)存储在一个泛型数组中,添加元素时若超过当前泛型数组容量,则以2倍扩容,进而实现List大小动态可变。(注:大小指容量,不是Count)

【基础必知】(请看代码注释)

  • 类及其变量
 public class List : IList, System.Collections.IList, IReadOnlyList
    {//继承了IList接口
        private const int _defaultCapacity = 4;//默认大小为4

        private T[] _items;//泛型数组,数据实际存储在这里
        [ContractPublicPropertyName("Count")]
        private int _size;//当前List中的元素个数,即Count
        private int _version;//版本号,在遍历时如果发现_version变了立即退出并抛出遍历过程集合被修改异常,比如在foreach里remove或add元素就会导致这个异常。更常见的是出现在多线程时一个线程遍历集合,另一个线程修改集合的时候
//每次改变数组,版本号都会加1
        [NonSerialized]
        private Object _syncRoot;

        static readonly T[] _emptyArray = new T[0];
}
  • 构造器
        public List()//构造器,初始化时容量为0。例如,List list=new List();
        {
            _items = _emptyArray;
        }

        public List(int capacity)//构造器,初始化时指定容量,例如List list = new List(10);
        {
            if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
            Contract.EndContractBlock();//输入的capacity不能为负

            if (capacity == 0)
                _items = _emptyArray;
            else
                _items = new T[capacity];//分配容量为capacity的连续地址
        }

        public List(IEnumerable collection)//构造器,将集合中的元素复制到List中,例如List list=new List(new int[3]{1,2,3})
        {
            if (collection == null)//集合不能为空
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
            Contract.EndContractBlock();

            ICollection c = collection as ICollection;//将IEnumerable转换为ICollection
            if (c != null)
            {
                int count = c.Count;
                if (count == 0)
                {
                    _items = _emptyArray;
                }
                else
                {
                    _items = new T[count];//分配和集合元素个数相同的容量
                    c.CopyTo(_items, 0);//该集合意实现了ICollection中的void CopyTo(T[] array, int arrayIndex)方法
                    _size = count;
                }
            }
            else
            {
                _size = 0;
                _items = _emptyArray;

                using (IEnumerator en = collection.GetEnumerator())
                {
                    while (en.MoveNext())
                    {
                        Add(en.Current);
                    }
                }
            }
        }
  • 属性Count和Capacity
        public int Capacity//属性,获取当前List的容量大小,设置容量大小时List中的数据被复制到新开辟的泛型数组内,复杂度O(n)
                           //实际扩容操作是在set方法中
        {
            get
            {
                Contract.Ensures(Contract.Result() >= 0);
                return _items.Length;
            }
            set
            {
                if (value < _size)//设置的value要大于当前数组中的元素个数
                {
                    ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
                }
                Contract.EndContractBlock();

                if (value != _items.Length)
                {
                    if (value > 0)
                    {
                        T[] newItems = new T[value];
                        if (_size > 0)
                        {
                            Array.Copy(_items, 0, newItems, 0, _size);//这意味着每次扩容要花费O(n)的时间,若估算问题中元素个数在10~15之间,应在初始化List大小而不用默认大小
                        }
                        _items = newItems;
                    }
                    else
                    {
                        _items = _emptyArray;
                    }
                }
            }
        }

        public int Count//只读属性属性,获取当前List中的元素个数
        {
            get
            {
                Contract.Ensures(Contract.Result() >= 0);
                return _size;
            }
        }
  • 添加元素Add
        public void Add(T item)//每次添加的元素在当前泛型数组的末尾,如果元素个数超出泛型数组容量,则扩容
                               //不扩容时复杂度O(1),扩容时O(n), 分摊O(1)
        {
            if (_size == _items.Length) EnsureCapacity(_size + 1);
            _items[_size++] = item;
            _version++;
        }
        private void EnsureCapacity(int min)//扩容方式,初始长度为0时添加了一个元素,则设置容量为默认容量4,往后以2倍的形式扩容
        {                                   //min表示当前所有元素需要的最小容量
            if (_items.Length < min)
            {
                int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Length * 2;
                // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
                // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
                if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
                if (newCapacity < min) newCapacity = min;//这行代码主要是添加集合AddRange时判断用,仅Add时可以去掉
                Capacity = newCapacity;//设置Capactity属性,在此进行了实际的扩容操作
            }
        }
  • 判断是否包含某元素Contains
   public bool Contains(T item)//判断是否包含某元素
        {
            if ((Object)item == null)
            {
                for (int i = 0; i < _size; i++)
                    if ((Object)_items[i] == null)
                        return true;
                return false;
            }
            else
            {
                EqualityComparer c = EqualityComparer.Default;
                for (int i = 0; i < _size; i++)//遍历数组查找,O(n)时间
                {
                    if (c.Equals(_items[i], item)) return true;//使用相等比较器来判断两元素是否相等,若类型 T 实现 IEquatable 泛型接口,
                                                               //则相等比较器是该接口的 Equals(T) 方法;否则,将使用默认的相等比较器Object.Equals(Object)
                                                               //Object.Equals(Object)等效于调用 Object.ReferenceEquals,会判断引用是否相等。
                }
                return false;
            }
        }
  • 移除元素Remove、RemoveAt
        public bool Remove(T item)//复杂度O(n)
        {
            int index = IndexOf(item);//查找该元素要线性搜索,O(n)
            if (index >= 0)
            {
                RemoveAt(index);
                return true;
            }

            return false;
        }

        public void RemoveAt(int index)
        {
            if ((uint)index >= (uint)_size)//输入的索引不能大于元素个数
            {
                ThrowHelper.ThrowArgumentOutOfRangeException();
            }
            Contract.EndContractBlock();
            _size--;//元素个数减1
            if (index < _size)//判断是否移除了最后一个元素
            {
                Array.Copy(_items, index + 1, _items, index, _size - index);//将后面的元素前移,复杂度为O(n)
            }
            _items[_size] = default(T);//最后一个元素恢复默认值
            _version++;
        }

        public int IndexOf(T item)//查找元素索引,返回第一个符合的元素的索引
        {                         //从0位置开始查找
            Contract.Ensures(Contract.Result() >= -1);
            Contract.Ensures(Contract.Result() < Count);
            return Array.IndexOf(_items, item, 0, _size);
        }
  • 清空列表Clear
        public void Clear()//清空列表,把_size赋值为0就可以,重新添加元素时,元素在数组中的位置由_size决定
        {
            if (_size > 0)
            {
                Array.Clear(_items, 0, _size); //这行代码不是必须的,其作用是清除元素以便gc可以回收引用
                _size = 0;
            }
            _version++;
        }
  • 将列表中的元素赋值到数组中CopyTo
  public void CopyTo(T[] array)//将List中的元素转到数组array中,实现调用了Array.Copy()
        {
            CopyTo(array, 0);
        }
        public void CopyTo(T[] array, int arrayIndex)
        {
            Array.Copy(_items, 0, array, arrayIndex, _size);//从_items的0位置处复制_size个元素放到array中,在arrayIndex处开始放置
        }
  • 索引器T[]
        public T this[int index]//索引器,实现IList接口中的方法,本质上是GetValue(int index),SetValue(Object value,int index)方法
        {
            get
            {
             
                if ((uint)index >= (uint)_size)//判断是否超出索引范围
                {
                    ThrowHelper.ThrowArgumentOutOfRangeException();
                }
                Contract.EndContractBlock();
                return _items[index];
            }

            set
            {
                if ((uint)index >= (uint)_size)
                {
                    ThrowHelper.ThrowArgumentOutOfRangeException();
                }
                Contract.EndContractBlock();
                _items[index] = value;
                _version++;
            }
        }

【常用方法】

  • 插入元素Insert、InsertRange(Add相当于Insert在最后一个位置)
        public void Insert(int index, T item)//将一个元素插入到index处,原来index处的元素后移
        {
            if ((uint)index > (uint)_size)//判断是否超出索引范围
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);
            }
            Contract.EndContractBlock();
            if (_size == _items.Length) EnsureCapacity(_size + 1);//涉及扩容
            if (index < _size)
            {
                Array.Copy(_items, index, _items, index + 1, _size - index); //原来index处的元素后移,复杂度O(n)
            }
            _items[index] = item;
            _size++;
            _version++;
        }

        public void InsertRange(int index, IEnumerable collection)//将集合中的元素插入List指定索引处
        {
            if (collection == null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
            }

            if ((uint)index > (uint)_size)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
            }
            Contract.EndContractBlock();

            ICollection c = collection as ICollection;
            if (c != null)
            {    // if collection is ICollection
                int count = c.Count;
                if (count > 0)
                {
                    EnsureCapacity(_size + count);//涉及扩容
                    if (index < _size)//把原来数组中index到index+count的元素后移
                    {
                        Array.Copy(_items, index, _items, index + count, _size - index);
                    }

                    // If we're inserting a List into itself, we want to be able to deal with that.
                    if (this == c)
                    {
                        // Copy first part of _items to insert location
                        Array.Copy(_items, 0, _items, index, index);
                        // Copy last part of _items back to inserted location
                        Array.Copy(_items, index + count, _items, index * 2, _size - index);
                    }
                    else
                    {
                        T[] itemsToInsert = new T[count];
                        c.CopyTo(itemsToInsert, 0);//先把集合中的元素复制到一个数组中
                        itemsToInsert.CopyTo(_items, index);//再将数组中的元素复制到泛型数组中,从index的位置开始
                    }
                    _size += count;
                }
            }
            else
            {
                using (IEnumerator en = collection.GetEnumerator())
                {
                    while (en.MoveNext())
                    {
                        Insert(index++, en.Current);
                    }
                }
            }
            _version++;
        }

        public void AddRange(IEnumerable collection)
        {
            Contract.Ensures(Count >= Contract.OldValue(Count));

            InsertRange(_size, collection);
        }
  • 查找某个元素的索引IndexOf(其实可以自己用for循环来实现,不必非要用这个)
        public int IndexOf(T item)//查找元素索引,返回第一个符合的元素的索引
        {                         //从0位置开始查找
            Contract.Ensures(Contract.Result() >= -1);
            Contract.Ensures(Contract.Result() < Count);
            return Array.IndexOf(_items, item, 0, _size);
        }

        public int IndexOf(T item, int index)//查找元素,从index开始查找
        {
            if (index > _size)
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
            Contract.Ensures(Contract.Result() >= -1);
            Contract.Ensures(Contract.Result() < Count);
            Contract.EndContractBlock();
            return Array.IndexOf(_items, item, index, _size - index);
        }

       public int IndexOf(T item, int index, int count)//查找元素,从index开始,只查找count个元素
        {
            if (index > _size)
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);

            if (count < 0 || index > _size - count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count);
            Contract.Ensures(Contract.Result() >= -1);
            Contract.Ensures(Contract.Result() < Count);
            Contract.EndContractBlock();

            return Array.IndexOf(_items, item, index, count);
        }
  • 将List转换成数组Toarray(跟CopyTo差不多)
        public T[] ToArray()
        {
            Contract.Ensures(Contract.Result() != null);
            Contract.Ensures(Contract.Result().Length == Count);

            T[] array = new T[_size];
            Array.Copy(_items, 0, array, 0, _size);
            return array;
        }
  • 排序Sort
        public void Sort()//排序使用默认比较器Array.Sort
        {
            Sort(0, Count, null);
        }


        public void Sort(IComparer comparer)//实现IComparer泛型接口,即实现int Compare(T x, T y);
        {
            Sort(0, Count, comparer);
        }

        public void Sort(int index, int count, IComparer comparer)//对列表中从index开始的Count个元素排序
        {
            if (index < 0)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
            }

            if (count < 0)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
            }

            if (_size - index < count)
                ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
            Contract.EndContractBlock();

            Array.Sort(_items, index, count, comparer);
            _version++;
        }

        public void Sort(Comparison comparison)//使用委托作为比较器 public delegate int Comparison(T x, T y);
        {
            if (comparison == null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
            }
            Contract.EndContractBlock();

            if (_size > 0)
            {
                IComparer comparer = new Array.FunctorComparer(comparison);
                Array.Sort(_items, 0, _size, comparer);
            }
        }
  • 列表翻转Reverse
        public void Reverse()
        {
            Reverse(0, Count);
        }

        public void Reverse(int index, int count)//将从index开始的Count个元素反转,实际调用了Array.Reverse,复杂度O(n)
        {
            if (index < 0)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
            }

            if (count < 0)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
            }

            if (_size - index < count)
                ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
            Contract.EndContractBlock();
            Array.Reverse(_items, index, count);
            _version++;
        }

【进阶】

  • 枚举器的实现(枚举器可用于读取集合中的数据,但不能用于修改集合)
        public struct Enumerator : IEnumerator, System.Collections.IEnumerator
        {
            private List list;
            private int index;
            private int version;
            private T current;//获取枚举数当前位置的元素

            internal Enumerator(List list)
            {
                this.list = list;
                index = 0;//枚举定位在集合中的第一个元素
                version = list._version;//记录当前集合中的版本号
                current = default(T);//当前元素为默认值
            }

            public void Dispose()//释放 List.Enumerator 使用的所有资源
            {
            }

            public bool MoveNext()//使枚举数前进到 List 的下一个元素
            {//如果 MoveNext 越过集合的末尾(即index=localList._size),则枚举器将定位到集合中的最后一个元素之后,MoveNext 返回 false
             //此时无法将 Current 设置为集合的第一个元素;必须改为创建新的枚举器实例

                List localList = list;

                if (version == localList._version && ((uint)index < (uint)localList._size))//判断版本号是否改变
                {
                    current = localList._items[index];
                    index++;
                    return true;
                }
                return MoveNextRare();
            }

            private bool MoveNextRare()
            {
                if (version != list._version)
                {
                    ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
                }

                index = list._size + 1;
                current = default(T);
                return false;
            }

            public T Current
            {
                get
                {
                    return current;
                }
            }

            Object IEnumerator.Current//在调用 Current 之前,MoveNext 返回相同的对象。 MoveNext 将 Current 设置为下一个元素
            {
                get
                {
                    if (index == 0 || index == list._size + 1)
                    {
                        ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
                    }
                    return Current;
                }
            }

            void  IEnumerator.Reset()//重置,恢复复默认值
            {
                if (version != list._version)
                {
                    ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
                }

                index = 0;
                current = default(T);
            }

        }

【参考】

MSDN

未完待续。。。

你可能感兴趣的:(C#,List源码)