zoukankan      html  css  js  c++  java
  • 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;//在遍历时如果发现_version变了立即退出并抛出遍历过程集合被修改异常,比如在foreach里remove或add元素就会导致这个异常。更常见的是出现在多线程时一个线程遍历集合,另一个线程修改集合的时候,相信很多人吃过苦头。

        [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操作吧。

  • 相关阅读:
    句子反转
    python中计时模块timeit的使用方法
    python入门(一)
    将小程序的API封装成支持Promise的API
    微信小程序实现导航功能的操作步骤
    微信小程序朋友转发和朋友圈分享
    js原生上传图片
    FormData
    原生 websocket
    判断手机终端是pc还是移动端,并自动跳转
  • 原文地址:https://www.cnblogs.com/mcyushao/p/10629611.html
Copyright © 2011-2022 走看看