C#源码—List数组


List数组源码窥探



List 属性


private T[] _items

  在开发过程中,List数组常被应用于数据、对象的存储,通过阅读List数组底层的实现代码,可以更好地了解List数组各种方法的实现原理、应用场合,从而正确的运用List数组,提高程序的运行效率,避免不必要的开销。
T类型数组:保存向List数组中添加的变量(从该变量的数组类型可以看出List数组底层使用Array数组来存储数据)

private T[] _items;

private int _size

该变量为private类型,不可直接访问,但在后续方法中反复出现

private int _size;

public int Count

该变量用于返回List数组中元素总数,为避免用户对List数组的长度随意修改,只赋予Count变量get属性访问器。

public int Count
        {
            get
            {
                return _size;
            }
        }

public int Capacity

该变量的返回值为在List数组存储的元素数量不超过该值情况下,内部开辟的数组空间大小,默认值为4,该值可用于用户手动对List数组扩容

public int Capacity
        {
            get
            {
                return _items.Length;
            }
            set
            {
                if (value < _size)
                {
                    ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
                }
                
                if (value == _items.Length)
                {
                    return;
                }

                if (value > 0)
                {
                	//开辟一个新的Array数组,该数组的大小为Capacity
                    T[] array = new T[value];
                    if (_size > 0)
                    {
                    	//扩容时将Array数组进行copy并赋给_items数组,当数据量较大时,不宜反复对数组进行扩容
                        Array.Copy(_items, 0, array, 0, _size);
                    }

                    _items = array;
                }
                else
                {
                    _items = _emptyArray;
                }
            }
        }

public T this[int index]

建立List数组类的索引


        public T this[int index]
        {
            get
            {
                if ((uint)index >= (uint)_size)
                {
                    ThrowHelper.ThrowArgumentOutOfRangeException();
                }

                return _items[index];
            }
            set
            {
                if ((uint)index >= (uint)_size)
                {
                    ThrowHelper.ThrowArgumentOutOfRangeException();
                }

                _items[index] = value;
                _version++;
            }
        }

List 初始化


public List()

第一种方法,即我们常用的无参调用构造函数

public List()
        {
            _items = _emptyArray;
        }

public List(int capacity)

第二种方法参数将赋给Capacity,即在初始化List数组时,可为List数组设置指定大小,该参数大小不影响Count值,但合理利用该参数可提高List数组的存储效率

 public List(int capacity)
        {
            if (capacity < 0)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
            }

            if (capacity == 0)
            {
                _items = _emptyArray;
            }
            else
            {
                _items = new T[capacity];
            }
        }

List 方法


private void EnsureCapacity(int min)

该方法虽不可访问,但对于理解List数组并提高对List数组使用效率上有重要作用。每次调用List数组的Add函数时,在插入数据前都会调用该方法,从而确保List中的Array数组没有超出边界。当Array数组没有额外的空间对数据进行存储时,会对当前容量扩容。从下段代码中不难看出,大多数情况下,都不会执行num=min这一行,因此Capacity将会直接等于num,而每次扩容时num都会变为之前的两倍。也就是说,必然会有0~1/2的数组容量未进行数据存储。当List存储的数据量较大时,这一部分“空闲”的空间将相当可观。因而不建议用List数组进行大量数据的存储。

  private void EnsureCapacity(int min)
        {
            if (_items.Length < min)
            {
                int num = (_items.Length == 0) ? 4 : (_items.Length * 2);
                if ((uint)num > 2146435071u)
                {
                    num = 2146435071;
                }

                if (num < min)
                {
                    num = min;
                }

                Capacity = num;
            }
        }

public void Add(T item)

从Add()方法的源码来看,在存储数据前先进行容量检查,避免超出数组边界。由此可以看出,虽然List数组底层采用Array数组来进行数据的存储,但据Capacity的内部实现代码来看,并非每次添加数据都重新new一个新的数组并用Copy为其赋值。因而List数组在添加数据方面可以说是以空间换时间

public void Add(T item)
        {
            if (_size == _items.Length)
            {
                EnsureCapacity(_size + 1);
            }

            _items[_size++] = item;
            _version++;
        }

public void Clear()

public void Clear()
        {
            if (_size > 0)
            {
                Array.Clear(_items, 0, _size);
                _size = 0;
            }
            _version++;
        }

public bool Contains(T item)

与List的IndexOf方法返回的元素下标不同,该方法用于返回一个bool类型的变量,用于确定List数组中是否含有item元素。需要特别注意的是,该函数可用于确定List数组中是否有存储为空的项。该方法检查元素时采用遍历的方法,若数据量较大时,该方法查找一次元素将会耗费较大的时间,尤其在List数组中不存在待检查元素时,需要将_size个元素全部遍历一遍

public bool Contains(T item)
        {
            if (item == null)
            {
                for (int i = 0; i < _size; i++)
                {
                    if (_items[i] == null)
                    {
                        return true;
                    }
                }

                return false;
            }

            EqualityComparer<T> @default = EqualityComparer<T>.Default;
            for (int j = 0; j < _size; j++)
            {
                if (@default.Equals(_items[j], item))
                {
                    return true;
                }
            }

            return false;
        }

public void CopyTo(T[] array)

public void CopyTo(T[] array)
        {
            CopyTo(array, 0);
        }

public void CopyTo(T[] array, int arrayIndex)

public void CopyTo(T[] array, int arrayIndex)
        {
            Array.Copy(_items, 0, array, arrayIndex, _size);
        }

public void CopyTo(T[] array, int arrayIndex)

public void CopyTo(T[] array, int arrayIndex)
        {
            Array.Copy(_items, 0, array, arrayIndex, _size);
        }

public int IndexOf(T item)

List数组IndexOf方法的所有重载均通过Array数组的IndexOf数组实现,IndexOf方法将在Array数组中介绍,在此不做过多解释

public int IndexOf(T item)
        {
            return Array.IndexOf(_items, item, 0, _size);
        }

public void Insert(int index, T item)

List数组的插入函数实现原理是将数组index之后的数据从index+1处开始Copy,最后给下标为index的区域赋item值

public void Insert(int index, T item)
        {
            if ((uint)index > (uint)_size)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);
            }

            if (_size == _items.Length)
            {
                EnsureCapacity(_size + 1);
            }

            if (index < _size)
            {
                Array.Copy(_items, index, _items, index + 1, _size - index);
            }

            _items[index] = item;
            _size++;
            _version++;
        }

public void RemoveAt(int index)

RemoveAt方法实现原理与Insert类似,用Copy方法将下标为index的元素覆盖

public void RemoveAt(int index)
        {
            if ((uint)index >= (uint)_size)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException();
            }

            _size--;
            if (index < _size)
            {
                Array.Copy(_items, index + 1, _items, index, _size - index);
            }

            _items[_size] = default(T);
            _version++;
        }


public bool Remove(T item)

Remove方法在移除item前会在数组中先对item进行查找并返回相应下标。在移除元素时,若已知下标,尽量使用RemoveAt方法

public bool Remove(T item)
        {
            int num = IndexOf(item);
            if (num >= 0)
            {
                RemoveAt(num);
                return true;
            }

            return false;
        }

public void TrimExcess()

网上关于TirmExcess方法的内容很少,但这个方法可以节省很大的内存空间,每次调用该方法时,会将当前的元素个数与List底层数组长度的百分之九十作比较,如果存储的元素个数足够少,则会删除Array数组中所有值为空的元素,在用List存储大量数据时建议调用该方法以节省内存空间

public void TrimExcess()
        {
            int num = (int)((double)_items.Length * 0.9);
            if (_size < num)
            {
                Capacity = _size;
            }
        }

public void Sort()

List数组的sort排序通过调用Array数组的Sort()方法来实现,具体原理及源码分析将放在Array数组篇中进行细说

public void Sort()
        {
            Sort(0, Count, null);
        }
public void Sort(int index, int count, IComparer<T> comparer)
        {
            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);
            }

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


  List数组在对少量数据的临时存储、删除、查找等方面发挥着重要作用,能在一定程度上缩短开发周期。但不宜用于大量数据的存储,容易造成内存空间上的额外开销(虽然可调用TrimExcess()方法进行优化)。List数组实质是顺序表,这也是List难以胜任大量数据存储的一大原因。增、删、改、查都会需要极大的时间开销来进行数组的遍历来查找相应元素。总的来说,List是开发过程中的一大利器。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值