Hash和红黑树以及其在C#中的应用
參考資料:
.Net 中HashTable,HashMap 和 Dictionary<key,value> 和List<T>和DataTable的比較 - 王若伊_恩賜解脫 - 博客園
c#HashSet源碼解析_fdyshlk的博客-CSDN博客_c# hashset
紅黑樹和哈希表的區(qū)別 - 安全技術(shù) - 億速云
一、基本概念
Hash和紅黑樹是兩種數(shù)據(jù)存儲的方式。除了這兩種存儲方式,最常用的是線性結(jié)構(gòu)存儲,比如數(shù)組和鏈表List等線性結(jié)構(gòu)。
對于線性存儲的數(shù)據(jù),如果想檢索一個數(shù)據(jù),需要把所有數(shù)據(jù)都遍歷一遍,然后找到你所需要的數(shù)據(jù)。這種方式對于大數(shù)據(jù)而言效率極低,其時間復(fù)雜度為O(n)。
二、哈希和紅黑樹基本原理
哈希(hash)也稱散列,通過散列算法變成固定的輸出到數(shù)組,所有的線性數(shù)據(jù)結(jié)構(gòu)中,數(shù)組的定位速度最快,因為它可通過數(shù)組下標(biāo)直接定位到相應(yīng)的數(shù)組空間,就不需要一個個查找。
紅黑樹的自旋是天才的設(shè)計,是一種特殊的平衡二叉樹數(shù)據(jù)結(jié)構(gòu),特點也是從幾十萬的數(shù)據(jù)里面幾步就能查找到,速度快。
三、使用場景
1、速度對比
物聯(lián)網(wǎng)可能是數(shù)百萬設(shè)備或者用戶聯(lián)網(wǎng),對高并發(fā)要求很大,所以,對網(wǎng)絡(luò)安全產(chǎn)品第一要求的是性能和速度。總體來說,哈希查找速度會比紅黑樹快,而且查找速度基本和數(shù)據(jù)量大小無關(guān),屬于常數(shù)級別;而RB樹的查找速度是log(n)級別。
紅黑樹查找和刪除的時間復(fù)雜度都是O(logn),Hash查找和刪除的時間復(fù)雜度都是O(1)。 如果紅黑樹的樹高度不深如小于8,采用的是數(shù)字查找,兩者性能沒有太多的差異。
也就是并非所有的場景,哈希都比紅黑樹快,要看代碼的優(yōu)化程度。hihttps使用的linux高并發(fā)EPOLL模式事件管理,就是紅黑樹。
2、數(shù)據(jù)預(yù)知
靜態(tài)數(shù)據(jù),可以基本預(yù)知大小,用哈希。如t初始化的規(guī)則就幾百條在可控范圍內(nèi),另外TOPIC黑白名單、URL地址等也不會太多,也是用的哈希就可以了。
動態(tài)數(shù)據(jù),如統(tǒng)計IP地址、任務(wù)調(diào)度、epoll高并發(fā)事件管理,無法判斷多少,可能很少,也可能巨多,用紅黑樹更佳。當(dāng)然,如果大概知道設(shè)備IP地址數(shù)量在一定范圍,如只有幾千,完全也可以用哈希。
3、內(nèi)存消耗
對內(nèi)存要求嚴(yán)格的地方,如嵌入式系統(tǒng),用紅黑樹。紅黑樹占用的內(nèi)存更小(僅需要為其存在的節(jié)點分配內(nèi)存),而哈希事先就應(yīng)該分配足夠的內(nèi)存存儲散列表,浪費內(nèi)存。
對內(nèi)存消耗無所謂的地方,如服務(wù)器有巨大的內(nèi)存,用哈希。哈希最大的缺點是內(nèi)存分配得小,可能元素就會沖突,沖突的元素大于8個成鏈表,效率還不如紅黑樹。 Java 的hashmap就是把哈希和紅黑樹結(jié)合在以前的。當(dāng)同一個hash值的節(jié)點數(shù)不小于8時,不再采用單鏈表形式存儲,而是采用紅黑樹。
4 復(fù)雜程度
哈希更簡單,紅黑樹算法復(fù)雜一點,不過這都不是事,大神早開源了很多穩(wěn)定的版本。
四、應(yīng)用場景總結(jié)
紅黑樹是有序的,哈希是無序的,根據(jù)項目需求來選擇,阿里巴巴的很多項目用紅黑樹更多,筆者認(rèn)為主要還是和內(nèi)存有關(guān),如果內(nèi)存要求苛刻的項目,就用紅黑樹;如果內(nèi)存足夠大,犧牲內(nèi)存換取更快的速度,哈希完全適合。
Hiihttps開源waf大量采用哈希算法,可能和速度并發(fā)要求有關(guān)。總之,數(shù)據(jù)結(jié)構(gòu)是網(wǎng)絡(luò)安全最基礎(chǔ)的學(xué)科。
五、C#中的應(yīng)用(HashTable和Dictionary)
首先:在.Net??模仿java 的過程中?? 拋棄了 HashMap ,所以以后再去面試.Net的時候當(dāng)別人問你HashTable 和HashMap 的區(qū)別的時候,請告訴他,C#.Net 中? 沒有HashMap。同時C#中也沒有Java中的TreeMap和TreeSet(即底層是由紅黑樹實現(xiàn)的數(shù)據(jù)結(jié)構(gòu))
HashTable?和 Dictionary?
數(shù)據(jù)結(jié)構(gòu)
Hashtable和Dictionary從數(shù)據(jù)結(jié)構(gòu)上來說都屬于Hashtable(哈希表),都是對關(guān)鍵字(鍵值)進行散列操作,將關(guān)鍵字散列到Hashtable的某一個槽位中去,不同的是處理碰撞的方法。散列函數(shù)有可能將不同的關(guān)鍵字散列到Hashtable中的同一個槽中去,這個時候我們稱發(fā)生了碰撞,為了將數(shù)據(jù)插入進去,我們需要另外的方法來解決這個問題。
采用鏈表法的是Dic??? 而采用開放尋址法(open addressing)-中? 雙重散列的方法的是 HashTable
至于這兩種數(shù)據(jù)結(jié)構(gòu)的使用方法??請自行閱讀算法導(dǎo)論?? 或者參照網(wǎng)上博客
但從底層的數(shù)據(jù)結(jié)構(gòu)可以發(fā)現(xiàn)
如果增刪的動作很多的話 推薦使用Dic? 因為解決碰撞的方式? 是List.Add
如果改動的動作很少? 查詢的動作很多的話?? 則推薦?使用HashTable? 因為映射查找之后? 只需要跳躍查找到? 碰撞后移動數(shù)據(jù)即可,另外當(dāng)增加數(shù)據(jù)太多時,開放尋址的擴容很耗費性能(請閱讀<算法導(dǎo)論>)
Dic 和HashTable使用比較
- 單線程程序中推薦使用 Dictionary, 有泛型優(yōu)勢, 且讀取速度較快, 容量利用更充分.
- 多線程程序中推薦使用 Hashtable, 默認(rèn)的 Hashtable 允許單線程寫入, 多線程讀取, 對 Hashtable 進一步調(diào)用 ?Synchronized() 方法可以獲得完全線程安全的類型. 而 Dictionary 非線程安全, 必須人為使用 lock 語句進行保護, ?效率大減.
- Dictionary 有按插入順序排列數(shù)據(jù)的特性 (注: 但當(dāng)調(diào)用 Remove() 刪除過節(jié)點后順序被打亂), 因此在需要體現(xiàn)順序的情境中使用 Dictionary 能獲得一定方便.?//Dic遍歷時? 會采用插入時的遍歷,而hashTable 采用遍歷時 則是打亂的
Hashtable 類和 Dictionary<TKey, TValue> 泛型類實現(xiàn) IDictionary 接口
Dictionary<TKey, TValue> 泛型類還實現(xiàn) IDictionary<TKey, TValue>泛型接口。
因此,這些集合中的每個元素都是一個鍵/值對。
Dictionary<TKey, TValue> 類與 Hashtable 類的功能相同
對于值類型,特定類型(不包括 Object)的 Dictionary<TKey, TValue> 的性能優(yōu)于 Hashtable,這是因為 Hashtable 的元素屬于 Object ?類型,所以在存儲或檢索值類型時通常發(fā)生裝箱和取消裝箱操作。
Dic? 和 List<T>
關(guān)于數(shù)據(jù)結(jié)構(gòu):
在前邊的比較已經(jīng)介紹了Dic?? 那么 List?<T> 的數(shù)據(jù)結(jié)構(gòu)是什么樣子的:
List<T>是 ArrayList 的泛型等效類(繼承了泛型接口)
堆中的樣子是這樣的
我們?yōu)榱擞懻摫闅v時Dictionary和List的效率,有個高人寫了個代碼,這是載圖
很明顯,LIST效率要好的多。
問題剖析
同樣是集合,為什么性能會有這樣的差距。我們要從存儲結(jié)構(gòu)和操作系統(tǒng)的原理談起。
首先我們清楚List<T>是對數(shù)組做了一層包裝,我們在數(shù)據(jù)結(jié)構(gòu)上稱之為線性表,而線性表的概念是,在內(nèi)存中的連續(xù)區(qū)域,除了首節(jié)點和尾節(jié)點外,每個節(jié)點都有著其唯一的前驅(qū)結(jié)點和后續(xù)節(jié)點。我們在這里關(guān)注的是連續(xù)這個概念。
而HashTable或者Dictionary,他是根據(jù)Key而根據(jù)Hash算法分析產(chǎn)生的內(nèi)存地址,因此在宏觀上是不連續(xù)的,雖然微軟對其算法也進行了很大的優(yōu)化。
由于這樣的不連續(xù),在遍歷時,Dictionary必然會產(chǎn)生大量的內(nèi)存換頁操作,而List只需要進行最少的內(nèi)存換頁即可,這就是List和Dictionary在遍歷時效率差異的根本原因。
所以根據(jù)value 的查找? dic 的效率是高于 List 的 但是遍歷的話?? 則Dic 要差點。這就好比你要摘抄書里邊的所有文字? 是根據(jù)目錄 查一個找一篇文章?快,還是直接從正文開始 從頭到尾快遍歷快一樣。單獨的找某一篇知道題目(key)的文章 當(dāng)然是從目錄快了
再談Dictionary
也許很多人說,既然Dictionary如此強大,那么我們?yōu)槭裁床挥肈ictionary來代替一切集合呢?
在這里我們除了剛才的遍歷問題,還要提到Dictionary的存儲空間問題,在Dictionary中,除了要存儲我們實際需要的Value外,還需要一個輔助變量Key,這就造成了內(nèi)存空間的雙重浪費。
而且在尾部插入時,List只需要在其原有的地址基礎(chǔ)上向后延續(xù)存儲即可,而Dictionary卻需要經(jīng)過復(fù)雜的Hash計算,這也是性能損耗的地方。
延伸:SortedList和SortedDictionary
SortedDictionary<TKey, TValue>泛型類是檢索O(log n)的二叉搜索樹,其中n是字典中的元素數(shù)。在這里,它類似于SortedList<TKey, TValue>泛型類。這兩個類有相似的對象模型,并且都有O(log n)檢索。這兩個類的不同之處在于內(nèi)存的使用以及插入和刪除的速度:
SortedList<TKey, TValue>比SortedDictionary<TKey, TValue >使用更少的內(nèi)存.
SortedDictionary<TKey, TValue>對于未排序的數(shù)據(jù)O(log n)具有更快的插入和刪除操作,而SortedList<TKey, TValue>的插入和刪除都是O(n)
如果列表是由已排序的數(shù)據(jù)一次填充的,那么SortedList<TKey, TValue>要比SortedDictionary<TKey, TValue>快。
兩者基本敘述:
- SortedList:是一個已序的數(shù)組(基于KeyValuePair的數(shù)組)。基于鍵值排序的鍵值對數(shù)組,使用二分查找(log n)檢索key,也可根據(jù)index檢索(log 1),add和remove都是o(n)。SortedList為了保持?jǐn)?shù)組的排序,它會移動位于插入的元素位置之后的所有元素(使用Array.Copy()),由于每次的插入都會重新排序,導(dǎo)致插入時的性能很差,因此并不推薦使用SortedList排序一個數(shù)組。
- SortedDictionary: 是一個BST,基于二叉查找樹實現(xiàn),使用二分查找檢索(key),add和remove都是o(log n)
延伸2 :c#中的數(shù)組、ArrayList、List區(qū)別
https://www.cnblogs.com/newcapecjmc/p/6970220.html
總結(jié)
以上是生活随笔為你收集整理的Hash和红黑树以及其在C#中的应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网络通信优化之通信协议:如何优化RPC网
- 下一篇: c# char unsigned_dll