C# List源码分析(二)
2016-02-24 10:36
537 查看
常用操作的复杂度分析
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 ? e==null : 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 ? get(i)==null : 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源码
相关文章推荐
- C# List源码分析(二)
- 以下C#程序的输出结果是( )。
- C# 委托事件
- C# default(T)
- 详解C#设计模式编程中的模板方法模式使用
- 第二章-C#基础
- c#读取xml文件配置文件Winform及WebForm-Demo具体解释
- C#Winform不重复的显示子窗体
- C#知识点汇总(未完成)
- C# 关键字extern用法
- C#调用windows api示例
- C#中利用正则表达式将人民币金额转换为大写汉字
- C#取整函数Math.Round、Math.Ceiling和Math.Floor
- POPTEST老李谈Debug和Release的区别(c#)
- C#使用简单邮件传输协议(SMTP)发送邮件
- C#递归
- c# 扩展方法奇思妙用基础篇八:Distinct 扩展
- C#读写记事本(txt)文件
- C#多线程学习(九)lock,Monitor,Mutex的区别
- 菜鸟学习C#语言之路——入门