1.Array 数组,
用于存储一组数据,创建数组需要明确数组存储的类型及长度,一旦确定不可改变,没有方便的添加,删除,查找方法。
特点:根据下标(索引)查找速度非常快,
时间复杂度:(增加:O(n),删除:O(n/2), 修改(n/2) 查找: O(1) )
2.ArrayList 动态数组,
可以根据元素个数的多少进行长度变化,可以存储任意类型,并且自身已经具备了常用的操作方法,Add(),Remove(),Insert(),Indexof(), 在读取,存储的过程中会涉及到装箱拆箱效率不高,
3.Stack 栈
栈是一种特殊的数据类型,先存储的元素最后被使用,这种操作通常称为先进后出(FILO),通常的操作只有两种,分别是入栈(压栈),出栈。两种操作的元素都在栈顶,
4 Queue 队列
队列是一种先进先出的数据类型,常用操作有两种,分别是入列出列,入列元素添加到队尾,出列的元素从队头取出
5.Hashtable 哈希表
是一种通过一个‘ 键 ’对应一个 ‘值’ 的形式进行数据存储,类似数组的下标访问,此时的下标可以自定义,
HashSet<T>(不重复的无序列表) , LinkedLIst<T>(),SortedSet<T> (不重复的有序列表) 掌握存储方式, 常用方法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) (并集)
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) (排除)
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,两者的区别是:
HashSet<T>.IntersectWith 是对当前集合进行修改,没有返回值;
IEnumerable<T>.Intersect并不修改原集合,而是返回了一个新的集合。
实例代码如下:
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>的操作方式。