.net集合类的研究--链表—ListDictionary,LinkedListT
鏈表是數據結構中存儲數據的一種形式,我們經常使用的List<T>,ArrayList,Hashtable等容器類,存取操作時是用數組Array來保存,ListDictionary和LinkedList<T>不用Array,而是用鏈表的形式來保存。
鏈表的優點和缺點
以ListDictionary為例,在源碼中,看不到Array類型的的變量,取而代之的是一個DictionaryNode類型的變量,查看該類的源碼會發現,只包含一個key,一個value,和一個DictionaryNode類型的next變量,DictionaryNode的代碼如下:
private class DictionaryNode {public object key;public ListDictionary.DictionaryNode next;public object value; }添加數據的時候,直接把當前節點的next變量賦值為新的節點,這樣一個節點扣一個節點,就有了鏈的形式。
在鏈表中查找數據時,如調用Contains(object key) :bool 方法,需要從鏈表的頭節點依次遍歷,逐個匹配,所以時間復雜度為O(n),和List<T>,ArrayList相比,在查詢效率上并沒有太大的區別。
那么鏈表的優勢在哪里呢?答案是,節省內存空間。
在之前的文章有提到過,線性表和哈希表初始化時會將內部Array數組默認一個大小,List<T>的初始值為4,Hashtable的為11,當添加數據碰到容量不足時,會將當前數組擴充2倍,這種做法不可避免要造成浪費。而鏈表不用數組保存,用節點相連,實實在在,添加幾個節點,就占用幾個節點的內存,相對于線性表和哈希表,鏈表沒有浪費,因而占用內存空間較少。
除了節省空間以外,鏈表還有一個優點,那就是插入數據的靈活性。
可惜這一點在ListDictionary中并沒有體現,每次添加數據,ListDictionary都要遍歷整個鏈表,來確保沒有重復節點,導致每次添加都要循環一次,添加數據的時間復雜度和查詢數據的時間復雜度都為O(n),比線性表和哈希表要慢的多。
HybridDictionary-結合鏈表和哈希表的特點揚長避短
在.net的集合容器中,有一個名為HybridDictionary的類,充分利用了Hashtable查詢效率高和ListDictionary占用內存空間少的優點,內置了Hashtable和ListDictionary兩個容器,添加數據時內部邏輯如下:
當數據量小于8時,Hashtable為null,用ListDictionary保存數據。
當數據量大于8時,實例化Hashtable,數據轉移到Hashtable中,然后將ListDictionary置為null。
HybridDictionary的Add方法的代碼如下:
public void Add(object key, object value) {if (this.hashtable != null){this.hashtable.Add(key, value);}else if (this.list == null){this.list = new ListDictionary(this.caseInsensitive ? StringComparer.OrdinalIgnoreCase : null);this.list.Add(key, value);}else if ((this.list.Count + 1) >= 9){//當數據量大于8時,則調用該方法,實例化Hashtable,轉移數據,清空listthis.ChangeOver();this.hashtable.Add(key, value);}else{this.list.Add(key, value);} }HybridDictionary類也進一步說明出了鏈表ListDictionary的特點:相對于Hashtable,占用內存較少,但隨著數據量的增加,查詢效率遠不及Hashtable。
泛型鏈表-LinkedList<T>
LinkedList是泛型鏈表,也是用節點存取,節點類型為LinkedListNode<T> ,與ListDictionary的節點不同的是,LinkedListNode<T>有next和prev兩個指向,說明LinkedList<T>是雙向鏈表,而ListDictionary是單向鏈表,代碼如下:
public sealed class LinkedListNode<T> {// Fieldsinternal T item;internal LinkedList<T> list;internal LinkedListNode<T> next;internal LinkedListNode<T> prev;...... }除了節省內存空間外,鏈表的另一個優點--插入數據的靈活性,在LinkedList<T>中完全體現出來,共有4個不同位置的添加數據的方法,分別為鏈頭插入,鏈尾插入,節點前插入,節點后插入。
每種插入方法又分別有兩種插入模式:
1、直接插入LinkedListNode<T>,沒有返回值。
2、直接插入T類型的值,返回插入完成后的節點。
四種位置,兩種模式,一共就有8個插入數據的方法,運用這些方法,可以在添加數據時靈活控制鏈表中數據的順序,這個優勢是線性表和哈希表所不能比的。代碼如下:
public LinkedListNode<T> AddAfter(LinkedListNode<T> node, T value); public void AddAfter(LinkedListNode<T> node, LinkedListNode<T> newNode); public void AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode); public LinkedListNode<T> AddBefore(LinkedListNode<T> node, T value); public void AddFirst(LinkedListNode<T> node); public LinkedListNode<T> AddFirst(T value); public LinkedListNode<T> AddLast(T value); public void AddLast(LinkedListNode<T> node);此外,由于LinkedList<T>是雙向鏈表,在查詢數據方面提供了“從前往后”和“從后往前”兩個查詢方法,所以雖然理論上鏈表的時間復雜度為O(n),根據自己在插入數據時對順序的把握,結合這兩個方法,可以相對提高查詢效率。
public LinkedListNode<T> Find(T value);//從前往后查 public LinkedListNode<T> FindLast(T value);//從后往前查結論
相對于線性表和哈希表,鏈表比較節省內存空間。
ListDictionary在每次添加數據時都要遍歷鏈表,效率較低,數據量較大且插入頻繁的情況下,不宜選用。
泛型鏈表LinkedList<T>在保證節省內存空間的同時,在添加數據的順序方面有極大的靈活性,加上泛型本身避免裝箱拆箱的優點,需要用鏈表的時候,應優先考慮泛型鏈表。
總結
以上是生活随笔為你收集整理的.net集合类的研究--链表—ListDictionary,LinkedListT的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果推自助维修计划 是要教用户修手机吗
- 下一篇: 需求分析的大道理