C#List源码
// C# 源碼
public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>
{
??? private const int _defaultCapacity = 4;//默認容量為4
?
??? private T[] _items;//list內(nèi)部是用數(shù)組實現(xiàn)的
??? [ContractPublicPropertyName("Count")]
??? private int _size;
??? private int _version;//在遍歷時如果發(fā)現(xiàn)_version變了立即退出并拋出遍歷過程集合被修改異常,比如在foreach里remove或add元素就會導致這個異常。更常見的是出現(xiàn)在多線程時一個線程遍歷集合,另一個線程修改集合的時候,相信很多人吃過苦頭。
??? [NonSerialized]
??? private Object _syncRoot;
?
??? static readonly T[]? _emptyArray = new T[0];???????
?
??? // 其他內(nèi)容???
?}
???? // 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的調(diào)用會有很多次,4->8->16->32->64->128->256,不但最后的容量中產(chǎn)生了大量的浪費,前面的一堆對象也都需要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操作優(yōu)先看的是能否找到該元素,如果能找到,將其移除,返回True,否則,返回false
C#的索引方法有點復雜,點到EqualityComparer里面看了一下索引的方法,這也是C#跟Java的不同之處了,Java的泛型里面是不能寫入Primitive類型的,因為Primitive類型其實是不繼承Object的,所以無法調(diào)用其中的equals方法。
但是C#是支持的,所以,會判斷元類型的Type,然后選取對應的Equals方法。
?
現(xiàn)在回頭看下RemoveAt方法,該方法仍然會調(diào)用Array.Copy操作,所以,可想而知刪除操作的復雜度了,內(nèi)存中平均刪除一個元素,要移動n/2個元素,復雜度為O(n)
?
而RemoveAll方法本身是復雜度為O(n)的,所以最好不要在循環(huán)中寫Remove操作吧。
轉載于:https://www.cnblogs.com/mcyushao/p/10629611.html
總結
- 上一篇: 重写父类中的方法
- 下一篇: c# 修改系统日期格式