哈希表--HashSet

.Net3.5以後出現了HashSet<T>,硬翻譯過來就是「哈希集合」,跟「哈希」兩字掛鉤說明這種集合的內部實現用到了哈希算法,用Reflector工具就能夠發現,HashSet<T>和Dictionary<TKey,TValue>使用了相同的存儲方式和哈希衝突算法,那麼,它跟Dictionary<TKey,TValue>和Hashtable在使用上到底有什麼不一樣?html

HashSet<T>對集合運算的操做

HashSet<T>是一個Set集合,雖然List、Collection也叫集合,但Set集合和它們卻大有不一樣。算法

HashSet<T>提供了和「Set集合運算」相關的方法,如:數組

IntersectWith (IEnumerable<T> other) (交集)工具

public void IntersectWithTest()
        {
            HashSet<int> set1 = new HashSet<int>() { 1, 2, 3 };
            HashSet<int> set2 = new HashSet<int>() { 2, 3, 4 };

            set1.IntersectWith(set2);

            foreach (var item in set1)
            {
                Console.WriteLine(item);
            }

            //輸出:2,3
        }

UnionWith (IEnumerable<T> other) (並集)spa

public void UnionWithTest()
        {
            HashSet<int> set1 = new HashSet<int>() { 1, 2, 3 };
            HashSet<int> set2 = new HashSet<int>() { 2, 3, 4 };

            set1.UnionWith(set2);

            foreach (var item in set1)
            {
                Console.WriteLine(item);
            }

            //輸出:1,2,3,4
        }

ExceptWith (IEnumerable<T> other) (排除).net

public void ExceptWithTest()
        {
            HashSet<int> set1 = new HashSet<int>() { 1, 2, 3 };
            HashSet<int> set2 = new HashSet<int>() { 2, 3, 4 };

            set1.ExceptWith(set2);

            foreach (var item in set1)
            {
                Console.WriteLine(item);
            }

            //輸出:1
        }

這些對集合的操做是List<T>、Hashtable和Dictionary<TKey,TValue>所缺乏的,可是伴隨着Linq和擴展方法的出現,.net 3.5爲泛型集合提供了一系列的擴展方法,使得全部的泛型集合具有了set集合操做的能力。翻譯

例如與HashSet的IntersectWith 方法對應的擴展方法是IEnumerable<T> 的Intersect,二者的區別是:code

HashSet<T>.IntersectWith 是對當前集合進行修改,沒有返回值;htm

IEnumerable<T>.Intersect並不修改原集合,而是返回了一個新的集合。blog

實例代碼以下:

public void IntersectTest()
        {
            HashSet<int> set1 = new HashSet<int>() { 1, 2, 3 };
            HashSet<int> set2 = new HashSet<int>() { 2, 3, 4 };

            IEnumerable<int> set3=set1.Intersect(set2);

            foreach (var item in set1)
            {
                Console.WriteLine(item);
            }

            foreach (var item in set3)
            {
                Console.WriteLine(item);
            }

            //輸出:o
            //set1 : 1,2,3
            //set3 : 2,3
        }

IEnumerable<T> 其餘的擴展方法也是同樣,都是不改變調用方法的數組,而是產生並返回新的IEnumerable<T>接口類型的數組,固然你能夠經過ToArray,ToList,ToDictionary將返回值轉換成你想要的集合類型。

至於如何使用這兩種集合操做方式,要取決於你的習慣和業務需求。

HashSet<T>的特色

在3.5以前,想用哈希表來提升集合的查詢效率,只有Hashtable和Dictionary<TKey,TValue>兩種選擇,而這兩種都是鍵-值方式的存儲。但有些時候,咱們只須要其中一個值,例如一個Email集合,若是用泛型哈希表來存儲,每每要在Key和Value各保存一次,不可避免的要形成內存浪費。而HashSet<T>只保存一個值,更加適合處理這種狀況。

此外,HashSet<T>的Add方法返回bool值,在添加數據時,若是發現集合中已經存在,則忽略此次操做,並返回false值。而Hashtable和Dictionary<TKey,TValue>碰到重複添加的狀況會直接拋出錯誤。

從使用上來看,HashSet<T>和線性集合List<T>更類似一些,但前者的查詢效率有着極大的優點。假如,用戶註冊時輸入郵箱要檢查惟一性,而當前已註冊的郵箱數量達到10萬條,若是使用List<T>進行查詢,須要遍歷一次列表,時間複雜度爲O(n),而使用HashSet<T>則不須要遍歷,經過哈希算法直接獲得列表中是否已存在,時間複雜度爲O(1),這是哈希表的查詢優點,在上一篇中已提到。

HashSet<T>的不能作的事情

HashSet<T>是Set集合,它只實現了ICollection接口,在單獨元素訪問上,有很大的限制:

跟List<T>相比,不能使用下標來訪問元素,如:list[1] 。

跟Dictionary<TKey,TValue>相比,不能經過鍵值來訪問元素,例如:dic[key],由於HashSet<T>每條數據只保存一項,並不採用Key-Value的方式,換句話說,HashSet<T>中的Key就是Value,假如已經知道了Key,也不必再查詢去獲取Value,須要作的只是檢查值是否已存在。

因此剩下的僅僅是開頭提到的集合操做,這是它的缺點,也是特色。

總結

綜上可知,HashSet<T>是一個Set集合,查詢上有較大優點,但沒法經過下標方式來訪問單個元素,這點會讓用慣了List<T>的人(我就是),用起來很不順手。

HashSet<T>有別於其餘哈希表,具備不少集合操做的方法,但優點並不明顯,由於.net 3.5以後擴展方法賦予了泛型集合進行集合操做的能力,但擴展方法的集合操做每每返回新的集合,在使用習慣上,我我的更偏心HashSet<T>的操做方式。

相關文章
相關標籤/搜索