.Net3.5以后出现了HashSet<T>,硬翻译过来就是“哈希集合”,跟“哈希”两字挂钩说明这种集合的内部实现用到了哈希算法,用Reflector工具就能够发现,HashSet<T>和Dictionary<TKey,TValue>使用了相同的存储方式和哈希冲突算法,那么,它跟Dictionary<TKey,TValue>和Hashtable在使用上到底有什么不一样?html
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将返回值转换成你想要的集合类型。
至于如何使用这两种集合操做方式,要取决于你的习惯和业务需求。
在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>是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>的操做方式。