C#中Dictionary,Hashtable,List的比較及分析


一. Dictionary與Hashtable

Dictionary與Hashtable都是.Net Framework中的字典類,能夠根據快速查找

二者的特性大體上是相同的,有時可以把Dictionary<K,V>看做是Hashtable的泛型版本。不過Hashtable是線程安全的,Dictionary是有序的。

字典的性能取決於鍵類型的GetHashCode()方法的實現代碼。

鍵類型也必須實現IEquatable<T>.Equals()方法,並且如果A.Equals(B)返回true,則A和B的GetHashCode()也必須返回相同的值。

 

Dictionary

  • 有泛型優勢(類型安全,性能更好),對於值類型,不存在裝箱和拆箱的性能損耗
  • 讀取速度快(體現在單條數據上)
  • 容量利用更充分
  • 有序(遍歷時輸出的順序就是加入的順序)

Hashtable

  • 適合多線程
  • 通過靜態方法Synchronize方法可獲得完全線程安全的類型
  • 無序

 

二.List與Dictionary

List有點類似於Dictionary。二者都具有使用泛型的優點,Dictionary沒有在內存中移動后續元素的性能開銷。

List是在數組的基礎上做的封裝,遍歷查詢更快(數據較多時),Dictionary單條查詢更快

引用某一篇文章的分析:

同樣是集合,為什么性能會有這樣的差距。我們要從存儲結構和操作系統的原理談起。 
首先我們清楚List<T>是對數組做了一層包裝,我們在數據結構上稱之為線性表,而線性表的概念是,在內存中的連續區域,除了首節點和尾節點外,每個節點都有着其唯一的前驅結點和后續節點。我們在這里關注的是連續這個概念。
而HashTable或者Dictionary,他是根據Key和Hash算法分析產生的內存地址,因此在宏觀上是不連續的,雖然微軟對其算法也進行了很大的優化。
由於這樣的不連續,在遍歷時,Dictionary必然會產生大量的內存換頁操作,而List只需要進行最少的內存換頁即可,這就是List和Dictionary在遍歷時效率差異的根本原因。
 
6. 再談Dictionary 
也許很多人說,既然Dictionary如此強大,那么我們為什么不用Dictionary來代替一切集合呢? 
在這里我們除了剛才的遍歷問題,還要提到Dictionary的存儲空間問題,在Dictionary中,除了要存儲我們實際需要的Value外,還需要一個輔助變量Key,這就造成了內存空間的雙重浪費。 
而且在尾部插入時,List只需要在其原有的地址基礎上向后延續存儲即可,而Dictionary卻需要經過復雜的Hash計算,這也是性能損耗的地方。 
 
 
簡單的做了下測試
using System.Collections;
using System.Collections.Generic;

namespace ConsoleApp
{
    class Program
    {
        public const int TOTAL_COUNT = 1000000;

        public static void Main(string[] args)
        {
            ListTest();
            DicTest();
            HashtableTest();
            ListInsertTest();
        }

        private static void HashtableTest()
        {
            Hashtable ht = new Hashtable();
            for (int i = 0; i < TOTAL_COUNT; i++)
            {
                ht.Add(i, new Model { Num = i });
            }
        }

        public static void ListTest()
        {
            List<Model> list = new List<Model>();
            for (int i = 0; i < TOTAL_COUNT; i++)
            {
                list.Add(new Model { Num = i });
            }
        }
        public static void ListInsertTest()
        {
            List<Model> list = new List<Model>();
            list.Insert(0, new Model { Num = 0 });
            list.Insert(1, new Model { Num = 1 });
            for (int i = 2; i < TOTAL_COUNT; i++)
            {
                list.Insert(1,new Model { Num = i });
            }
        }

        public static void DicTest()
        {
            Dictionary<int, Model> dic = new Dictionary<int, Model>();
            for (int i = 0; i < TOTAL_COUNT; i++)
            {
                dic.Add(i, new Model { Num = i });
            }
        }

        public class Model
        {
            public int Num { set; get; }
        }
    }
}
測試代碼

 

這個測試總共有四個插入數據方式:Hashtable.Add,Dictionary.Add,List.Add,List.Insert
字典中插入的Key都是int類型。 結果如下
 
方法中插入次數都是100W  因為實在等不下去了,就中途結束了。
不過這基本不影響分析結果,因為拖后腿的是List.Insert,在只執行了28W的情況下就已經占到了總耗時的95%。
從結果中可以看到插入效率依次是 List.Add -> Dictionary.Add -> Hashtable.Add -> List.Insert
這個結果也符合上面的分析。
 
下面在實測一下遍歷
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp
{
    class Program
    {
        public const int TOTAL_COUNT = 1000000;

        public static void Main(string[] args)
        {
            List<Model> list = new List<Model>();
            for (int i = 0; i < TOTAL_COUNT; i++)
            {
                list.Add(new Model { Num = i });
            }

            Hashtable ht = new Hashtable();
            for (int i = 0; i < TOTAL_COUNT; i++)
            {
                ht.Add(i,new Model { Num = i });
            }

            Dictionary<int, Model> dic = list.ToDictionary(l=>l.Num);

            ListTest(list);
            DicTest(dic);
            HashtableTest(ht);
        }

        private static void HashtableTest(Hashtable ht)
        {
            foreach (var key in ht.Keys)
            {
                var rst = ht[key];
            }
        }

        public static void ListTest(List<Model> list)
        {
            foreach (var item in list)
            {
                var rst = item;
            }
        }

        public static void DicTest(Dictionary<int, Model> dic)
        {
            foreach (var key in dic.Keys)
            {
                var rst = dic[key];
            }
        }

        public class Model
        {
            public int Num { set; get; }
        }
    }
}
遍歷測試代碼

 

還是100W條數據

 
List -> Dictionary -> Hashtable
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM