Collections你用对了吗?

    .Net有两类基础的集合类型:ListDictionaryList是基于Index的,Dictionary是基于key的。集合类型一般实现了IEnumberableICollection或者Ilist 接口。

     

    类型

    描写叙述

    使用场景

    ArrayList

    可以依据加入项动态调整集合大小。

      • 适用于存储自己定义类型,特别是对于数据常常进行添加和删除的。
      • 使用TrimToSize()去掉预留元素位置,优化性能。

      • 使用ArrayList.BinarySearch进行高效的查询。
      • 不要用ArrayList去存储string。用StringCollection取代。
      • ArrayList.Sort()默认使用的是高速排序。

    Hashtable

    基于Keyhash值。存储key/value对的collection类型。

      • 适用于存储大量并不常常更改的数据,数据常常修改会造成额外的性能开销(计算hash值)。

      • 适用于常常Search而不须要排序的情况。

    HybridDictionary

    可以依据集合的大小,动态使用对应类型的字典类型。使用ListDictionary(小)。使用Hashtable(大)。

      • 适用于常常进行Search操作。

      • 不要用于排序。

    ListDictionary

    基于Keyhash值,存储key/value对的collection类型,基于单链表实现的。

      • 对于不大于10key/value对的排序很高效。

       

    NameValueCollection

    存储string类型的key/value的可排序的Collection

      • string类型。可排序。能够在一个集合中包括多个同样Key的实体。
      • 适用于常常改变数据。
      • 适用于去缓存数据项以便于高速取回。

    SortedList

    基于Key排序的Key/value对的collection。能够通过keyindex来提取value

      • 适用于存储静态数据,而且在一段时间内仅仅有少量数据须要更新。
      • 能够用于高速依据索引或者Key来取回数据。
      • 避免用于存储数据有大量更新的数据。创建和排序的时间很耗时。
      • 避免存储string类型。由于过度的转换开销。使用StringCollection 取代。

    StringCollection

    string类型的arraylist

      • 适用于存储常常变化的string类型的数据,而且须要在大块中检索。
      • 适合绑定string类型数据到DataGrid,避免了向下类型转换的开销。

    StringDictionary

    基于Hash table

    用于存储keystring类型的Dictionary

      • 适用于存储须要常常检索的静态string类型数据

    Queue

    基于ICollection的实现。先进先出

     

    Stack

    后进先出

     

     

    集合类型常见问题:

    • Boxing issues
    • Thread safety
    • Enumeration overhead

     

     

    重要 Boxing issues

     

    假设使用ArrayList去存储值类型时都会被运行装箱操作。当取数据时会运行拆箱操作。

    当要存储大量的item时会导致过度开销。

    ArrayList al = new ArrayList();

    for (int i = 0; i < 1000; i++)

    al.Add(i); //Implicitly boxed because Add() takes an object

    int f = (int)al[0]; // The element is unboxed

     

    解决方式:

    考虑使用强类型的数组取代。或者使用自己定义集合类型。

    .net framework 2.0之后,使用泛型能够避免装箱和拆箱的开销。

     

    重要 Thread Safety

     

    通常,集合默认不是线程安全的。在多线程下对于读操作是线程安全的,对于不论什么改动操作是非线程安全的,会导致未知的结果。

     

    解决方式:

    1. 使用Synchronized方法

    //初始化

    ArrayList arrayList = new ArrayList();

    //加入对象到集合

    …….

    //使用Synchronized方法

    Arraylist syncArrayList = ArrayList.Synchronized(arrayList);

    1. 使用Lock语句的SyncRoot属性

    ArrayList myCollection = new ArrayList();

    Lock(myCollection.SyncRoot){}

     

    重要 Enumeration Overhead

     

    .Net Framework 1.1 通过重写IEnumerable.GetEnumberator 方法提供了枚举器。

    但也不是非常理想,理由例如以下:

    • GetEnumerator方法是虚拟的,所以无法内联。

    • GetEnumerator方法返回的是Ienumerator的接口而不是一个实际的类型。因此,在编译期间,无法确定实际的枚举器(Enumerator)
    • MoveNext方法和Current属性相同是虚拟的,因此无法内联。
    • IEnumerator.Current返回的的是Object类型。依据存储在集合的数据类型可能须要装箱和拆箱操作。

     

    因此在使用foreach对集合进行操作时会面临托管堆和虚拟方法的开销。

     

    重要 Collection Guidelines

     

    • Analyze your requirements before choosing the collection type.(选择集合类型之前进行需求分析)
    • Initialize collections to the right size when you can.(当你能确定集合的大小时,初始化集合时指定合适的大小)
    • Consider enumerating overhead.(考虑枚举开销)
    • Prefer to implement IEnumerable with optimistic concurrency.(实现IEnumerable接口去实现开放式并发)
    • Consider boxing overhead.(考虑装箱和拆箱开销)
    • Consider for instead of foreach.(考虑使用for取代foreach
    • Implement strongly typed collections to prevent casting overhead.(实现强类型的集合类型去防止强制转换开销)
    • Be efficient with data in collections.(高效利用集合中的数据)

     

     

    Analyze your requirements before choosing the collection type.

     

    • Do you need to sort your collection?

      • ArrayList适用于仅仅读已排序的数据作为数据源
      • SortedList适用于须要排序的静态不常常更新的数据,通常。(当构造集合时SortedList预先排序数据,创建排序列举的过程是比較昂贵的。可是不论什么对数据的更新都能自己主动且高效的对集合进行又一次排序)
      • NameValueCollection适用于须要排序的字符串集合。

    • Do you need to search your collection?

      • 假设你会依据key/value随机的进行search,使用HashTable

      • 假设对字符串进行随机的search,使用StringDictionary

      • 对于集合大小小于10的进行search使用ListDictonary
    • Do you need to access each element by index?

      • 对于基于0索引開始的集合。使用ArrayListStringCollection
      • 通过Key来取数据,使用HashTable,SortedList,ListDictionary,StringDictionary
      • NameValueCollection既能够使用索引又能够通过key取数据。
      • 集合(Array)通过索引取数据比不论什么集合类型都高效。

    • Do you need a custom collection?

      • 当你想通过引用类型封送集合。
      • 你须要创建一个强类型集合来避免转换开销(假设创建的强类型集合是继承自CollectionBase 或者Hashtable。你依然无法避免转换开销。)。

      • 你须要创建一个仅仅读集合。

      • 你须要对你的强类型集合提供序列化。
      • 你须要注意枚举的开销。

     

    Initialize collections to the right size when you can.

     

    • 尽可能的指定集合的大小,能够更好的提高集合性能。

     

    Consider enumerating overhead.

     

    • 假设实现了Ienumerable.GetEnumerator,同一时候实现一个非虚拟的GetEnumerator方法。

    class MyClass : IEnumerable

    {

      // non-virtual implementation for your custom collection

      public MyEnumerator GetEnumerator() {

        return new MyEnumerator(this); // Return nested public struct

      }

      // IEnumerator implementation

      public IEnumerator.GetEnumerator() {

        return GetEnumerator();//call the non-interface method

      }

    }

    foreach调用非虚拟的GetEnumerator会比通过接口调用虚拟方法更高效。

    • 实现IEnumerator.Current属性。

     

    // Custom property in your class

    //call this property to avoid the boxing or casting overhead

    Public MyValueType Current {

      MyValueType obj = new MyValueType();

      // the obj fields are populated here

      return obj;

    }

    // Explicit member implementation

    Object IEnumerator.Current {

    get { return Current} // Call the non-interface property to avoid casting

    }

     

    Prefer to implement IEnumerable with optimistic concurrency.

    • 确保集合不会被改动当集合正在被枚举。
    • 採用快照在枚举器中。

     

    Consider boxing overhead.

    当在集合中存储值类型时,装箱开销会依据集合的大小。更新或操作数据的频率影响。

    假设不须要集合过多的功能,尽量使用Array.

     

    Consider for instead of foreach.

    在性能敏感的代码中尽量使用for.

     

     

    Implement strongly typed collections to prevent casting overhead.

     

    Be efficient with data in collections.

    当处理大量的对象时。处理每一个对象的大小会十分重要。

     

     

     


posted @ 2016-02-29 19:14  lcchuguo  阅读(292)  评论(0编辑  收藏  举报