C# 泛型集合

 原文出處個人wiki,轉載請說明出處
html

 

考慮到泛型在集合類型中的普遍應用,這裏一塊兒討論。git

1. 泛型

1.1 泛型的定義與約束

建立泛型方法、委託、接口或類時,須要在名稱後增長尖括號及其中的泛型參數,泛型參數一般用T或T爲前綴的描述性單詞表示。程序員

    /// <summary>
    /// 集合類型擴展方法
    /// </summary>
    public static class CollectionExt
    {
        /// <summary>
        /// 集合類型判空
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty<T>(this ICollection<T> collection)
        {
            return collection == null || collection.Count == 0;
        }
    }

可用where來約束泛型繼承的基類或實現的接口,而且可使用default(T)獲取泛型的默認值。github

        /// <summary>
        /// 獲取集合中按照某個數值類型比較 最小的元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="items"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        public static T MinItem<T>(this IEnumerable<T> items, Func<T, decimal> func) where T : class
        {
            List<decimal> list = new List<decimal>();
            if (items.IsNullOrEmpty())
            {
                return null;
            }

            decimal minValue = decimal.MaxValue;
            T minItem = default(T);
            foreach (var item in items)
            {
                var currentValue = func(item);
                if (minValue > currentValue)
                {
                    minItem = item;
                    minValue = currentValue;
                }
            }
            return minItem;
        }

1.2 泛型中的類型膨脹

面試的時候,經常會被問到C#和JAVA中泛型的差別,以及泛型參數類型在什麼時候肯定。在C#中,仍然以第一段代碼中集合類型判空爲例分析。面試

    class Program
    {
        static void Main(string[] args)
        {
            var values = new List<int>();
            var strs = new List<string>();
            Console.WriteLine(values.IsNullOrEmpty());
            Console.WriteLine(strs.IsNullOrEmpty());
            Console.ReadKey();
        }
    }

查看IL代碼。數組

  .entrypoint
  // 代碼大小       44 (0x2c)
  .maxstack  1
  .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<int32> values,
           [1] class [mscorlib]System.Collections.Generic.List`1<string> strs)
  IL_0000:  nop
  IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
  IL_0006:  stloc.0
  IL_0007:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
  IL_000c:  stloc.1
  IL_000d:  ldloc.0
  IL_000e:  call       bool ConsoleApplication1.CollectionExt::IsNullOrEmpty<int32>(class [mscorlib]System.Collections.Generic.ICollection`1<!!0>)
  IL_0013:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_0018:  nop
  IL_0019:  ldloc.1
  IL_001a:  call       bool ConsoleApplication1.CollectionExt::IsNullOrEmpty<string>(class [mscorlib]System.Collections.Generic.ICollection`1<!!0>)
  IL_001f:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_0024:  nop
  IL_0025:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  IL_002a:  pop
  IL_002b:  ret

能夠看到,泛型的參數類型在編譯時就已經肯定。安全

編譯後生成的List`1<int32>和List`1<string>,是兩個不一樣的類型,這稱爲類型膨脹,基於這種方法實現的泛型被稱爲真實泛型。性能

這就使得類型安全,避免了裝箱和拆箱操做。學習

2. 集合類型

System.Array定義了數組類。

System.Collections命名空間中定義了處理object對象的經常使用集合類,在訪問時需類型轉換或裝箱拆箱。

System.Collections.Generic命名空間中提供了不少集合類的泛型版本。

System.Collections.Specialized命名空間中定義了專用於特定類型的集合類。

.NET 4.0開始提供System.Collections.Concurrent命名空間中的多個線程安全的集合類。

不急,慢慢來。ui

 

以前遇到幾個工做七八年的居然也不懂這些很基礎的東西。

能夠說劣幣驅逐良幣,也是離開目前公司的主要緣由,不過社會自己如此。無論怎樣,仍是多看看、多學習,進而跳出牢籠,從新認識自我和社會,纔有可能抓住轉瞬即逝的機會。

從自身的角度,以爲技術和成長好厲害、解決不少問題,實際上是沒有用滴,老闆考慮的始終是用較爲低廉的薪水招到能作通常事情的人就好了,畢竟你們基本都是業務驅動的公司,能賣錢纔是正道理,因此能說的永遠比能作事情的機會多

2.1 IEnumerable

簡單的說,實現此接口,才能支持遍歷,接口返回的是一個IEnumerator。

    [ComVisible(true)]
    [Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")]
    public interface IEnumerable
    {
        [DispId(-4)]
        IEnumerator GetEnumerator();
    }

 2.2 IEnumerator

真正的實現了遍歷的過程,還延伸到yield的用法,這裏再也不贅述。

    [ComVisible(true)]
    [Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")]
    public interface IEnumerator
    {
        object Current { get; }

        bool MoveNext();

        void Reset();
    }

Current當前枚舉項的引用,初始化時還沒有指向任何枚舉項,每次訪問時要轉化爲對應類型,故屢次使用當前枚舉項時可先賦值給強類型的臨時變量,但泛型版本則不須要這樣作。

MoveNext()移動引用使其指向下一個枚舉項,首次移動後指向第一個枚舉項,重複整個過程直至當前枚舉項爲空實現對集合的遍歷。

Reset()將當前枚舉項的引用重置到初始狀態。

 

從頭至尾對一個集合進行枚舉本質上並非一個線程安全的過程,即便一個集合自己是線程安全的,其餘線程仍能夠修改該集合,這將致使枚舉數引起異常。若是說寫七八年代碼的連這都不懂,我信(被前前同事坑怕了,中毒太深)。

若要在枚舉過程當中保證線程安全,能夠在整個枚舉過程當中鎖定集合。

2.3 ICollection

    [ComVisible(true)]
    public interface ICollection : IEnumerable
    {
        int Count { get; }

        bool IsSynchronized { get; }

        object SyncRoot { get; }

        void CopyTo(Array array, int index);
    }
各類實現類中,有幾個比較關鍵的屬性:
Capacity屬性表示的是集合的容量。 Count屬性表示的是集合中當前數據項個數,按照各自的規則自動擴容。 Clear操做只是清空集合中的元素,但集合的容量沒變,仍然佔用着內存空間,相似於乘客從列車中出來,但列車的容量不變。 SyncRoot屬性主要在須要線程安全的操做時使用。

2.4 IDictionary

屬於一對一的字典,Keys和Values獨立存儲,經過對Key執行哈希獲取Value的存儲位置,因此Key能夠是非null的任意類型,且不能有重複。

    [ComVisible(true)]
    [DefaultMember("Item")]
    public interface IDictionary : ICollection, IEnumerable
    {
        object this[object key] { get; set; }

        bool IsReadOnly { get; }

        ICollection Keys { get; }

        ICollection Values { get; }

        void Add(object key, object value);

        void Clear();

        bool Contains(object key);

        IDictionaryEnumerator GetEnumerator();

        void Remove(object key);
    }

在具體的實現類中:

Dictionary<TKey,TValue>採用鏈地址法處理哈希衝突,填充因子是1。

ConcurrentDictionary<TKey,TValue>線程安全的鍵值集合,TyrAdd、TryGetValue、TryRemove、TryUpdate方法以非阻塞的方式訪問元素。

SortedList<TKey,TValue>用兩個數組分別存儲鍵和值並保持大小的同步性,因此對GC比較友好一點,且支持用索引或鍵查找值並按照鍵排序。

SortedDictionary<TKey,TValue>是一個按照鍵構造的二叉查找樹,因此,添加或刪除操做只會根據紅黑樹的特性旋轉節點來保持平衡故性能優於SortedList而差於Dictionary,而查找時可利用二分法,結構上對GC而言相對比較複雜。

 

其中,Dictionary[key]=value至關於AddOrUpdate,屬於引用替換,親們能夠考慮下是否是線程安全的。

 2.5 ILookup

屬於一對多的字典類型。

    [DefaultMember("Item")]
    public interface ILookup<TKey, TElement> : IEnumerable<IGrouping<TKey, TElement>>, IEnumerable
    {
        IEnumerable<TElement> this[TKey key] { get; }

        int Count { get; }

        bool Contains(TKey key);
    }

2.6 IList

提供對數據項的增刪操做,可按照索引訪問數據項。

    [ComVisible(true)]
    [DefaultMember("Item")]
    public interface IList : ICollection, IEnumerable
    {
        object this[int index] { get; set; }

        bool IsFixedSize { get; }

        bool IsReadOnly { get; }

        int Add(object value);

        void Clear();

        bool Contains(object value);

        int IndexOf(object value);

        void Insert(int index, object value);

        void Remove(object value);

        void RemoveAt(int index);
    }

索引符是一種特殊類型的屬性,提供按照索引訪問數據項的功能。

遍歷刪除的操做,能夠考慮使用倒敘方式。

 2.7 ISet

不容許有重複的元素。

    public interface ISet<T> : ICollection<T>, IEnumerable<T>, IEnumerable
    {
        bool Add(T item);

        void ExceptWith(IEnumerable<T> other);

        void IntersectWith(IEnumerable<T> other);

        bool IsProperSubsetOf(IEnumerable<T> other);

        bool IsProperSupersetOf(IEnumerable<T> other);

        bool IsSubsetOf(IEnumerable<T> other);

        bool IsSupersetOf(IEnumerable<T> other);

        bool Overlaps(IEnumerable<T> other);

        bool SetEquals(IEnumerable<T> other);

        void SymmetricExceptWith(IEnumerable<T> other);

        void UnionWith(IEnumerable<T> other);
    }

這裏牢騷幾句,好多程序員處理去重時,都用dictionary(鍵值同樣),明明有set不用,真捉急。

還有一些場景,好比電商產品起價的更新時,須要對數據取交集、並集、差集的運算,此時set也頗有妙用,能夠將複雜度從N^2下降到N。

 

更多知識體系...

相關文章
相關標籤/搜索