C# List源码分析(二)

来源:互联网 发布:思想实验 知乎 编辑:程序博客网 时间:2024/06/05 05:26

常用操作的复杂度分析

Contains

该方法是一个O(n)的方法,是根据顺序依次遍历整个列表的,观看源码,跟JAVA还是有不少分别的,在上一篇中就有发现,因为C#对Primitive类型是有处理的,所以跟JAVA代码略有不同

// Contains returns true if the specified element is in the List.// It does a linear, O(n) search.  Equality is determined by calling// item.Equals().//public bool Contains(T item) {    if ((Object) item == null) {        for(int i=0; i<_size; i++)            if ((Object) _items[i] == null)                return true;        return false;    }    else {        EqualityComparer<T> c = EqualityComparer<T>.Default;        for(int i=0; i<_size; i++) {            if (c.Equals(_items[i], item)) return true;        }        return false;    }}

代码中使用了EqualityComparer,就是对Primitive类型进行了额外的处理,否则他们并非继承Object,则没有Equals和Hashcode方法。
以下是JAVA的代码

/** * Returns <tt>true</tt> if this list contains the specified element. * More formally, returns <tt>true</tt> if and only if this list contains * at least one element <tt>e</tt> such that * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>. * * @param o element whose presence in this list is to be tested * @return <tt>true</tt> if this list contains the specified element */public boolean contains(Object o) {    return indexOf(o) >= 0;}/** * Returns the index of the first occurrence of the specified element * in this list, or -1 if this list does not contain the element. * More formally, returns the lowest index <tt>i</tt> such that * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>, * or -1 if there is no such index. */public int indexOf(Object o) {    if (o == null) {        for (int i = 0; i < size; i++)            if (elementData[i]==null)                return i;    } else {        for (int i = 0; i < size; i++)            if (o.equals(elementData[i]))                return i;    }    return -1;}

Java是通过调用Object的equals方法来实现的,也从侧面说明了为什么JAVA的泛型其实是不支持primitive类型了。
另外还有一个细节,C#对Contains是针对泛型的方法,而JAVA是针对Object的方法。
不过忽略掉Primitive的泛型,JAVA代码的简洁上,比C#确实要好些,用起来麻烦一些

Insert

Java中的Insert是一个重载方法,其实就是对Add方法的重载
C#则是额外抽象了一个方法叫做Insert,因为Insert指定了索引,所以,insert操作对数组是有copy操作的,所以复杂度为O(n)
而且同时有可能因为插入造成数组大量的copy从而进行Capacity的倍增

// Inserts an element into this list at a given index. The size of the list// is increased by one. If required, the capacity of the list is doubled// before inserting the new element.// public void Insert(int index, T item) {    // Note that insertions at the end are legal.    if ((uint) index > (uint)_size) {        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);    }    Contract.EndContractBlock();    if (_size == _items.Length) EnsureCapacity(_size + 1);    if (index < _size) {        Array.Copy(_items, index, _items, index + 1, _size - index);    }    _items[index] = item;    _size++;                _version++;}

这里使用了ThrowHelper这个静态类,最开始,还以为是他们为了让代码可读,所以使用工厂方法,更易用
但是后来发现不是这门回事,是为了在中间层次优化集合类而做的。

// This file defines an internal class used to throw exceptions in BCL code.// The main purpose is to reduce code size. // // The old way to throw an exception generates quite a lot IL code and assembly code.// Following is an example://     C# source//          throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));//     IL code://          IL_0003:  ldstr      "key"//          IL_0008:  ldstr      "ArgumentNull_Key"//          IL_000d:  call       string System.Environment::GetResourceString(string)//          IL_0012:  newobj     instance void System.ArgumentNullException::.ctor(string,string)//          IL_0017:  throw//    which is 21bytes in IL.// // So we want to get rid of the ldstr and call to Environment.GetResource in IL.// In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the// argument name and resource name in a small integer. The source code will be changed to //    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key, ExceptionResource.ArgumentNull_Key);//// The IL code will be 7 bytes.//    IL_0008:  ldc.i4.4//    IL_0009:  ldc.i4.4//    IL_000a:  call       void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument)//    IL_000f:  ldarg.0//// This will also reduce the Jitted code size a lot. //// It is very important we do this for generic classes because we can easily generate the same code // multiple times for different instantiation. // // <

在IL层次上,如果使用throw关键字,那么编译出来的IL代码很多,然后,如果使用静态方法,他们IL大量代码将被重用,从而减少IL代码的大小。

InsertRange

InsertRange 方法,在指定位置插入若干元素,该方法和JAVA中的addAll方法有些类似,不过不同的是,C#中的插入的参数是必须实现IEnumerable< T >接口的,但是Java中,参数是必须实现Collection接口的,这也导致了两者方法中的不同

C# 针对Collection,对操作进行了优化,在内存copy上,只是进行了一次,但是如果是自己的类实现了IEnumerable接口的话,操作复杂度则大大增加,会频繁调用insert操作

自己实现了IEnumerable接口的话,切忌使用insertRange方法,复杂度太高

// Inserts the elements of the given collection at a given index. If// required, the capacity of the list is increased to twice the previous// capacity or the new size, whichever is larger.  Ranges may be added// to the end of the list by setting index to the List's size.//public void InsertRange(int index, IEnumerable<T> collection) {    if (collection==null) {        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);    }    if ((uint)index > (uint)_size) {        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);    }    Contract.EndContractBlock();    ICollection<T> c = collection as ICollection<T>;    if( c != null ) {    // if collection is ICollection<T>        int count = c.Count;        if (count > 0) {            EnsureCapacity(_size + count);            if (index < _size) {                Array.Copy(_items, index, _items, index + count, _size - index);            }            // If we're inserting a List into itself, we want to be able to deal with that.            if (this == c) {                // Copy first part of _items to insert location                Array.Copy(_items, 0, _items, index, index);                // Copy last part of _items back to inserted location                Array.Copy(_items, index+count, _items, index*2, _size-index);            }            else {                T[] itemsToInsert = new T[count];                c.CopyTo(itemsToInsert, 0);                itemsToInsert.CopyTo(_items, index);                                }            _size += count;        }                    }    else {        using(IEnumerator<T> en = collection.GetEnumerator()) {            while(en.MoveNext()) {                Insert(index++, en.Current);                                                }                        }    }    _version++;            }

SynchronizedList

这个类实现了IList接口,只是所有的方法都加上了同步操作,看代码结构就知道是典型的代理模式啊

internal class SynchronizedList : IList<T> {    private List<T> _list;    private Object _root;    /// other}

在初始化的时候,使用的lock用的对象则是List中的SyncRoot,这样每次增加了同步操作,保证了线程的安全性
Java的线程安全的List是Vector,和C#不同的是,没有用代理模式,而是所有方法全部重写了,仍然是ArrayList一个结构,不同的是方法上都加了synchronized

以下为List源码地址

List源码
Array源码

0 0
原创粉丝点击