标签:
一、前言
首先谈谈泛型,包括Java, C++都有自己的泛型(模版),这种机制大大的减少了代码的数量,是一种类型的抽象。集合就我了解C++的 STL 中的vector<T>, list<T>, map<T,T> 等, .net 中的List<T>, HashTable<T,T>等,都是对基本数据结构的实现,如链表,队列,栈,等。但是在具体使用中,不同的语言,如果使用不当,我造成严重的性能影响,合格的程序员应该了解这些性能陷阱。
二、.net 泛型
在.net中通过使用泛型,我们可以达到以下两个目的:
1.Type safe 2. No Boxing.
这个比较好理解,举个例子ArrayList, 其源码如下:
public class ArrayList : IEnumerable, ICollection, IList { private object[] items; private int size; public ArrayList(int initalCapacity) { items = new object[initalCapacity]; } public void Add(object item) { if (size < items.Length - 1) { items[size] = item; ++size; } else { //Allocate a larger array, copy the elements to there. } } public object this[int index] { get { if (index < 0 || index >= size) throw new IndexOutOfRangeException(); return items[index]; } set { if (index < 0 || index >= size) throw new IndexOutOfRangeException(); } } // ommit other details }
可见在Add的时候会有装箱操作发生,如果存放的是1,000,000的Point, 将会有大量的内存被浪费掉(8M (extra)+ 8M(data) + 4M(reference)), 除了因为装箱引起内存浪费外,因为我们相关的操作时基于System.Object,类型安全也是一个大问题。
泛型可以完美的解决这个问题,原理看简化的源码:
public class List<T> : IEnumerable<T>, ICollection<T>, IList<T> { private T[] items; private int size; public List(int initalCapacity) { // does this work? items = new T[initalCapacity]; } public void Add(T item) { if (size < items.Length - 1) { items[size] = item; ++size; } else { //Allocate a larger array, copy the elements to there. } } public T this[int index] { get { if (index < 0 || index >= size) throw new IndexOutOfRangeException(); return items[index]; } set { if (index < 0 || index >= size) throw new IndexOutOfRangeException(); items[index] = value; } } // ommit other details }
不用担心类型安全和装箱拆箱的问题了。但是如果我增加一些比较的功能呢?
public static int BinarySearch<T>(T[] array, T element) {
//At some point in the algorithm, we need to compare:
if (array[x] < array[y]) {
...
}
}
System.Object没有实现 static operator <, 对于上面这个函数,大部分都会类型都会编译错误。我们可以使用模版函数的限制功能,来保证T实现了 比较的 , .NET一种有5种限制:
public class Widget{ public void Display(int i, int j) { } } public class GenericDemo { // T must implement an interface public string Format<T>(T instance) where T: IFormattable { return instance.ToString("N", CultureInfo.CurrentCulture); // OK, T must have IFormattable.ToString(...) } // T must based on a base class public void Display<T>(T widget) where T : Widget { widget.Display(1, 2); } // T must a parameterless cosntructor public T Create<T>() where T : new() { return new T(); } // T must be a reference type: public void ReferencesOnly<T>(T reference) where T : class { } // T must be a value type: public void ValueType<T>(T valueType) where T : struct { } }
这样我们可以这样写BinarySearch了:
public static int BinarySearch<T>(T[] array, T element) where T : IComparable<T> {
//At some point in the algorithm, we need to compare:
int x = 1; int y = 2;
if (array[x].CompareTo(array[y]) < 0) {
//...
}
}
接下来我们再讨论 IEquatable<T>,先看下面这个函数:
public static void CallEquals<T>(T instance) { instance.Equals(instance); }
Equals将会调用基类的虚函数Equals,它的参数是System.Object,会产生装箱。但是我们实现了IEquatable<T>,使用在模版限定中, 就可以避免装箱了
//From the .NET Framework: public interface IEquatable<T> { bool Equals(T other); } public static void CallEquals<T>(T instance) where T : IEquatable<T> { instance.Equals(instance); }
这个函数将不再调用虚函数的Equals, 这样就避免了装箱。我们在前一篇文章中,提到valueType的最佳实践中,要现实IEquatable<T> 就是这个原因。 那么按理说所有的集合最好都限制为IEquatable<T>类型,但是为了扩展性的考虑,我们用组合的形式,委托给GenericEqualityComparer, 举个例子List<T>.Contains, 前看简化源码:
public bool Contains(T item) { if (item == null) { for (int i = 0; i < this._size; i++) { if (this._items[i] == null) { return true; } } return false; } EqualityComparer<T> @default = EqualityComparer<T>.Default; for (int j = 0; j < this._size; j++) { if (@default.Equals(this._items[j], item)) { return true; } } return false; }
把比较委托给了EqualityComaprer<T>, 如果我们替换了它,就可以改变我们的比较策略了。除了equals,这里还有别的实现数学的泛型知识。请看参考链接
三、参考
<<Pro .NET Performance>>
http://www.codeproject.com/Articles/8531/Using-generics-for-calculations
标签:
原文地址:http://www.cnblogs.com/ming11/p/4542197.html