C#List源码

  1. List

// C# 源码

public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>

{

    private const int _defaultCapacity = 4;//默认容量为4

    private T[] _items;//list内部是用数组实现的

    [ContractPublicPropertyName("Count")]

    private int _size;

    private int _version;

    [NonSerialized]

    private Object _syncRoot;

    static readonly T[]  _emptyArray = new T[0];       

    // 其他内容   

 }

     // C# Code

// 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++;

}

// 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;

    }

}

// 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;

            }

        }

    }

}

C# 中 List默认的容量其实是4,所以最好还是初始化容量吧,可以想象,如果一个列表里面有129个元素,那么代码中对Capacity的调用会有很多次,4->8->16->32->64->128->256,不但最后的容量中产生了大量的浪费,前面的一堆对象也都需要GC搞定了。也就是252个对象。浪费还是很严重的。

// Removes the element at the given index. The size of the list is

// decreased by one.

public bool Remove(T item) {

    int index = IndexOf(item);

    if (index >= 0) {

        RemoveAt(index);

        return true;

    }

    return false;

}

// Returns the index of the first occurrence of a given value in a range of

// this list. The list is searched forwards from beginning to end.

// The elements of the list are compared to the given value using the

// Object.Equals method.

//

// This method uses the Array.IndexOf method to perform the

// search.

//

public int IndexOf(T item) {

    Contract.Ensures(Contract.Result<int>() >= -1);

    Contract.Ensures(Contract.Result<int>() < Count);

    return Array.IndexOf(_items, item, 0, _size);

}

// Array.cs

public static int IndexOf<T>(T[] array, T value, int startIndex, int count) {

    if (array==null) {

        throw new ArgumentNullException("array");

    }

    if (startIndex < 0 || startIndex > array.Length ) {

        throw new ArgumentOutOfRangeException("startIndex",

            Environment.GetResourceString("ArgumentOutOfRange_Index"));

    }

    if (count < 0 || count > array.Length - startIndex) {

        throw new ArgumentOutOfRangeException("count",

            Environment.GetResourceString("ArgumentOutOfRange_Count"));

    }

    Contract.Ensures(Contract.Result<int>() < array.Length);

    Contract.EndContractBlock();

    return EqualityComparer<T>.Default.IndexOf(array, value, startIndex, count);

}

// Removes the element at the given index. The size of the list is

// decreased by one.

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++;

}

从代码来看,remove操作优先看的是能否找到该元素,如果能找到,将其移除,返回True,否则,返回false

C#的索引方法有点复杂,点到EqualityComparer里面看了一下索引的方法,这也是C#跟Java的不同之处了,Java的泛型里面是不能写入Primitive类型的,因为Primitive类型其实是不继承Object的,所以无法调用其中的equals方法。

但是C#是支持的,所以,会判断元类型的Type,然后选取对应的Equals方法。

现在回头看下RemoveAt方法,该方法仍然会调用Array.Copy操作,所以,可想而知删除操作的复杂度了,内存中平均删除一个元素,要移动n/2个元素,复杂度为O(n)

而RemoveAll方法本身是复杂度为O(n)的,所以最好不要在循环中写Remove操作吧。

猜你喜欢

转载自www.cnblogs.com/mcyushao/p/10629611.html