天天看點

java--HashSet詳解

哈希表–HashSet

.Net3.5之後出現了HashSet,硬翻譯過來就是“哈希集合”,跟“哈希”兩字挂鈎說明這種集合的内部實作用到了雜湊演算法,用Reflector工具就可以發現HashSet和Dictionary <\TKey,TValue>使用了相同的存儲方式和哈希沖突算法,那麼,它跟Dictionary <\TKey,TValue>和Hashtable在使用上到底有什麼不同?

HashSet對集合運算的操作

HashSet是一個Set集合,雖然List、Collection也叫集合,但Set集合和它們卻大有不同。

HashSet提供了和“Set集合運算”相關的方法,如:

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

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

            set1.IntersectWith(set2);

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

            //輸出:2,3
        }
UnionWith (IEnumerable<T> other) (并集)

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

            set1.UnionWith(set2);

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

            //輸出:1,2,3,4
        }
ExceptWith (IEnumerable<T> other) (排除)

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

            set1.ExceptWith(set2);

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

            //輸出:1
        }
           

這些對集合的操作是List、Hashtable和Dictionary<\TKey,TValue>所缺少的,但是伴随着Linq和擴充方法的出現,.net 3.5為泛型集合提供了一系列的擴充方法,使得所有的泛型集合具備了set集合操作的能力。

例如與HashSet的IntersectWith 方法對應的擴充方法是IEnumerable 的Intersect,兩者的差別是:

HashSet.IntersectWith 是對目前集合進行修改,沒有傳回值;

IEnumerable.Intersect并不修改原集合,而是傳回了一個新的集合。

執行個體代碼如下:

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

            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 其他的擴充方法也是一樣,都是不改變調用方法的數組,而是産生并傳回新的IEnumerable接口類型的數組,當然你可以通過ToArray,ToList,ToDictionary将傳回值轉換成你想要的集合類型。

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

HashSet的特點

在3.5之前,想用哈希表來提高集合的查詢效率,隻有Hashtable和Dictionary<\TKey,TValue>兩種選擇,而這兩種都是鍵-值方式的存儲。但有些時候,我們隻需要其中一個值,例如一個Email集合,如果用泛型哈希表來存儲,往往要在Key和Value各儲存一次,不可避免的要造成記憶體浪費。而HashSet隻儲存一個值,更加适合處理這種情況。

此外,HashSet的Add方法傳回bool值,在添加資料時,如果發現集合中已經存在,則忽略這次操作,并傳回false值。而Hashtable和Dictionary<\TKey,TValue>碰到重複添加的情況會直接抛出錯誤。

從使用上來看,HashSet和線性集合List更相似一些,但前者的查詢效率有着極大的優勢。假如,使用者注冊時輸入郵箱要檢查唯一性,而目前已注冊的郵箱數量達到10萬條,如果使用List進行查詢,需要周遊一次清單,時間複雜度為O(n),而使用HashSet則不需要周遊,通過雜湊演算法直接得到清單中是否已存在,時間複雜度為O(1),這是哈希表的查詢優勢,在上一篇中已提到。

HashSet的不能做的事情

HashSet是Set集合,它隻實作了ICollection接口,在單獨元素通路上,有很大的限制:

跟List相比,不能使用下标來通路元素,如:list[1] 。

跟Dictionary<\TKey,TValue>相比,不能通過鍵值來通路元素,例如:dic[key],因為HashSet每條資料隻儲存一項,并不采用Key-Value的方式,換句話說,HashSet中的Key就是Value,假如已經知道了Key,也沒必要再查詢去擷取Value,需要做的隻是檢查值是否已存在。

是以剩下的僅僅是開頭提到的集合操作,這是它的缺點,也是特點。

總結

綜上可知,HashSet是一個Set集合,查詢上有較大優勢,但無法通過下标方式來通路單個元素,這點會讓用慣了List的人(我就是),用起來很不順手。

HashSet有别于其他哈希表,具有很多集合操作的方法,但優勢并不明顯,因為.net 3.5之後擴充方法賦予了泛型集合進行集合操作的能力,但擴充方法的集合操作往往傳回新的集合,在使用習慣上,我個人更偏愛HashSet的操作方式。

轉載:http://www.cnblogs.com/bianlan/p/3147629.html