Analysis of the underlying source code of List in Unity

1、Analysis of the underlying code of List

List is one of the most common scalable array components in C#, and we often use it to replace arrays. Because it is scalable, we do not need to manually allocate the size when writing the program. Next, let's take a look at the underlying implementation of the list.

public class list<T> :IList <T>,System.Collections.IList, IReadOnlyList <T>
{
    private const int _ defaultCapacity = 4;
    private T[] _items;
    private int _size;
    private int _version;
    private Object _syncRoot;
    static readonly T[] _emptyArray = new T[0];

    //构建一个列表,该列表最初是空的,容量为0
    //将第一个元素添加到列表后,容量将增加到16,然后根据需要以2的倍数增加
    public List()
    {
        _items = _emptyArray;
    }
    
    //构造具有给定容量的List。该列表最初为空的。但是在需要重新分配之前,会为给定数量的元素流出空间
    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]; 
    }

    //******其他内容
}

From the constructor part, we can know that List is implemented internally using an array instead of a linked list, and when no specified capacity is given, the initial capacity is 0. In other words, when the List component is called by the two functions Add() and Remove(), it uses the operation of "transferring elements on the array, or copying and generating them from the original array." to a new array" works.

2.Add interface analysis

Add interface source code is as follows

//将给定对象添加到此列表的末尾。列表的大小增加1
//如果需要,再添加新元素之前,列表的容量会增加1
public void Add(T item)
{
    if(_size == _items.Length) EnsureCapacity(_size + 1);
    _items[_size++] = item;
    _version++;
}

//如果列表的当前容量小于min,则容量将增加到当前容量的两倍或min,以较大者为准
private void EnsureCapacity(int min)
{
    if(_items.Length < min){
        int newCapacity = _items.Length == 0?_defaultCapacity : _items.Length * 2;
    
        //在遇到溢出前,允许列表增加到最大可能的容量(约2GB元素)
        //请注意,即使_items.Length由于(uint)强制转化而溢出,次检查任然有效
        if((uint)newCapacity > Array.MaxArrayLength) 
            newCapacity = Array.MaxArrayLength;
        if(newCapacity < min) 
            newCapacity = min;
        Capacity = newCapacity
    }
}

In the Add function in the above List source code, each time the data of an element is added, the Add interface will first check whether the capacity is sufficient. If it is not enough, the EnsureCapacity function is called to increase the capacity. Every time the capacity is not enough, the capacity of the entire array will be doubled. _defaultCapacity indicates that the default size of the capacity is 4, so the expansion route is 4, 8, 16, 32, 64, 128, 256, 512, 1024.... ...and so on.

List uses array form as the underlying data structure. The advantage is that it is very fast to obtain elements using indexing. The disadvantage is that it will be very bad when expanding. Every new operation on the array will cause memory garbage, which will put a great burden on garbage collection (GC).

The expansion method of multiples of 2 here can reduce the burden on the GC, but if the array is continuously applied for expansion, it will still cause a lot of burden on the GC, especially when the List in the code frequently uses Add. In addition, if the quantity is used improperly, a large amount of memory space will be wasted. For example, when the number of elements is 520, the List will be expanded to 1024 elements. If the remaining 504 space units are not used, most of the memory space will be wasted.

3.Remove interface analysis

The following is the source code of the Remove interface

//删除给定索引处的元素。列表的大小减1
public bool Remove(T item)
{
    int index = IndexOf(item);
    if(index >= 0)
    {
        RemoveAt(index);
        return true;
    }

    return false;
}

//返回此列表范围内给定值首次出现的索引
//该列表从头到尾向前搜索
//使用Object.Equals方法将列表中的元素与给定值进行比较
//此方法使用Array.IndexOf方法进行搜索
public int IndexOf(T item)
{
    Contract.Ensures(Contract.Result<int>() >= -1)
    Contract.Ensures(Contract.Result<int>() < Count)
    return Array.IndexOf(_items, item, 0, _size);
}

//删除给定索引处的元素,列表的大小减1
public void RemoveAt(int index)
{
    if((uint)index >= (uint)_size)
    {
        ThrowHelper.ThrowArgumentOutOfRangeException();
    }
    Contract.EndContractBlock();
    _size--;
    if(index < _size)
    {
        Array.Copy(_items, index + 1, _items, index, _size - index);
    }
    _items[_size] = default(T);
    _version++;
}

The Remove function contains the IndexOf and RemoveAt functions. The IndexOf function is used to find the index position of the element, and the RemoveAt function can be used to delete the element at the specified position.

As you can see from the source code, the principle of element deletion is to use Array.Copy to overwrite the array. IndexOf uses the Array.IndexOf interface to find the index position of an element. The internal implementation of this interface itself is to compare each position in index order from 0 to n.

Guess you like

Origin blog.csdn.net/qq_42720695/article/details/124951471