.net源码解读之List<T>

我们知道,List与数组的区别是,可以Add元素,但是这是如何实现的呢?
翻看源码,解开面纱,发现List的内部实现,就是使用数组实现的。
List的源码地址:https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,cf7f4095e4de7646

部分源码片段解答:

public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>
 {
     private const int _defaultCapacity = 4;

     private T[] _items;
     [ContractPublicPropertyName("Count")]
     private int _size;
     private int _version;
     [NonSerialized]
     private Object _syncRoot;
     
     static readonly T[]  _emptyArray = new T[0];
 } 

其中,_items变量中便是存储的List中的元素
_size就是List中的元素个数

无参构造函数

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

// Constructs a List. The list is initially empty and has a capacity
// of zero. Upon adding the first element to the list the capacity is
// increased to 16, and then increased in multiples of two as required.
public List() {
    _items = _emptyArray;
}

这时候,_items变量为长度为0的数组

有参构造函数(传入容量)

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

// Constructs a List with a given initial capacity. The list is
// initially empty, but will have room for the given number of elements
// before any reallocations are required.
// 
public List(int capacity) {
    if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
    Contract.EndContractBlock();

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

如果传入的容量是0,则_items为长度为0的数组,否则,为capacity大小的数组(无任何元素)

有参构造函数(传入一个集合)

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

// Constructs a List, copying the contents of the given collection. The
// size and capacity of the new list will both be equal to the size of the
// given collection.
// 
public List(IEnumerable<T> collection) {
    if (collection==null)
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
    Contract.EndContractBlock();

    ICollection<T> c = collection as ICollection<T>;
    if( c != null) {
        int count = c.Count;
        if (count == 0)
        {
            _items = _emptyArray;
        }
        else {
            _items = new T[count];
            c.CopyTo(_items, 0);
            _size = count;
        }
    }    
    else {                
        _size = 0;
        _items = _emptyArray;
        // This enumerable could be empty.  Let Add allocate a new array, if needed.
        // Note it will also go to _defaultCapacity first, not 1, then 2, etc.
        
        using(IEnumerator<T> en = collection.GetEnumerator()) {
            while(en.MoveNext()) {
                Add(en.Current);                                    
            }
        }
    }
}

如果传入的集合为空,则抛出异常
如果传入的集合长度为0,则_items为长度为0的数组
如果集合有内容,则把集合的内容添加到_items中,这时,_size=items.Lenth

Add方法

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

// Adds the given object to the end of this list. The size of the list is
// increased by one. If required, the capacity of the list is doubled
// before adding the new element.
//
public void Add(T item) {
    if (_size == _items.Length) EnsureCapacity(_size + 1);
    _items[_size++] = item;
    _version++;
}

从注释可以看到:

  • 如果需要,_items的容量会增大一倍
  • 每次添加一个元素,_size会+1

其中_items的容量增大一倍,在EnsureCapacity函数中实现:
https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,405

// Ensures that the capacity of this list is at least the given minimum
// value. If the currect capacity of the list is less than min, the
// capacity is increased to twice the current capacity or to min,
// whichever is larger.
private void EnsureCapacity(int 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;
        Capacity = newCapacity;
    }
}

可以看到

  • _items的长度为0,则扩容时,长度为_defaultCapacity也就是4
  • _items的长度不为0,则扩容时,长度为_items.Lenth*2
  • 最后赋值Capacity = newCapacity,跳转到Capacity的set中:

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

// Gets and sets the capacity of this list.  The capacity is the size of
// the internal array used to hold items.  When set, the internal 
// array of the list is reallocated to the given capacity.
// 
public int Capacity {
    get {
        Contract.Ensures(Contract.Result<int>() >= 0);
        return _items.Length;
    }
    set {
        if (value < _size) {
            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);
                }
                _items = newItems;
            }
            else {
                _items = _emptyArray;
            }
        }
    }
}

扩容时,New了一个新的数组,长度为Capacity,将_items之前的元素(如果有)复制到新的数组。然后将新的数组赋值到_items

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值