前言
前面几篇文章讲了泛型、讲了数组,都有提到集合,这一节重点对集合进行详细解说。本文主要使用各种集合类型。以至于评估其性能,针对不同的场景选择不同的集合使用。
集合分类详解
一、列表
列表的创建
代码语言:javascript复制var intList=new List<int>();
代码语言:javascript复制
创建一个容量为10 的集合
代码语言:javascript复制List<int> intList=new List<int>(10);
代码语言:javascript复制
列表初始值设定项
代码语言:javascript复制var intList = new List<int>() { 1,2};
代码语言:javascript复制
添加元素(通过使用Add方法进行元素添加)
代码语言:javascript复制var intList=new List<int>();
inList.Add(1);
var intList = new List<int>();
intList.AddRange(new List<int>(){ 1,2,3});
代码语言:javascript复制
插入元素(使用insert方法插入元素)
代码语言:javascript复制var intList = new List<int>();
intList.Insert(3, 3);
var intList = new List<int>();
intList.InsertRange(3, new List<int>() { 1,2,3});
访问元素(通过下表去实现访问或者循环遍历)
代码语言:javascript复制var intList = new List<int>();
intList.AddRange( new List<int>() { 1,2,3});
var answer = intList[2];
foreach (var item in intList)
{
Console.WriteLine(item);
}
代码语言:javascript复制
删除元素(通过RemoveAt指定删除某一位置的值)
代码语言:javascript复制var intList = new List<int>();
intList.AddRange( new List<int>() { 1,2,3,4});
intList.RemoveAt(3);
代码语言:javascript复制
搜索(通过IndexOf访问指定位置的值,可以使用的方法IndexOf()、LastIndexOf()、FindLastIndex()、Find()、FindLast())
代码语言:javascript复制var intList = new List<int>();
intList.AddRange( new List<int>() { 1,2,3,4});
int indenx = intList.IndexOf(3);
代码语言:javascript复制
二、队列
队列是其元素以先进先出(FirstIn,FirstOut,FIFO)的方式来处理的集合,先放入队列中的元素会先读取。队列的例子比比皆是。排队买票,食堂排队打饭,先到先买,先到先打饭。
队列使用System.Collections.Generic命名空间中的泛型类Queue<T>来实现的。这个类型类似于List<T>。队列类使用Enqueue()方法在队列的一段插入元素,使用Dequeue()方法在另一端读取元素并删除。
代码语言:javascript复制Queue<string> qa = new Queue<string>();
qa.Enqueue("第一个进");
qa.Dequeue();
代码语言:javascript复制
其中使用Count()返回队列中的元素总数,使用Peek()方法从队列头部读取一个元素,但不删除它。使用TrimExcess()方法重新设置队列的容量,Dequeue()虽然删除了元素,但不会重新设置容量。要从队列头部去除空元素,应使用TrimExcess()方法
三、栈
栈是与队列非常相似的另一个容器,知识使用不同的方法访问栈,而且栈的元素属于最后添加的元素最先读取也就是后进先出(LastIn,FirstOut,LIFO)。
与Queue<T>类似,栈使用Stack<T>来实现,其中Push()在栈中添加元素,用Pop()方法获取最近添加的元素。
代码语言:javascript复制Stack<string> qa = new Stack<string>();
qa.Push("第一个进");
qa.Pop();
代码语言:javascript复制
其中其他方法与Queue<T>类似,使用Count()返回队列中的元素总数,使用Peek()方法从队列头部读取一个元素,但不删除它。使用Contains()确定某个元素是否存在于栈中,存在则返回True
四、有序列表
如果需要基于键对所需的集合进行排序,就可以使用SortedList<TKey,TValue>类。这个类按照键给的元素排序,这个集合中的值和键都可以使用任意类型。
下面先创建一个空列表,然后通过Add()方法进行添加元素。然后输出结果。我们看下图可以发现自动帮我们已经排序好了然后输出的。
代码语言:javascript复制static void Main(string[] args)
{
var test = new SortedList<string, string>();
test.Add("First","Code1");
test.Add("Two", "Code2");
test.Add("A", "Code3");
foreach (var item in test)
{
Console.WriteLine(item.Key ":" item.Value);
}
}
代码语言:javascript复制
五、字典
字典表示一种复杂的数据结构,这种数据结构允许按照某个键来访问元素。字典也称为映射或散列表。字典的主要特性是能根据键快速查找值。也可以自由添加和删除元素,这有点像List<T>,但没有在内存中移动后续元素的性能开销。
字典的初始化:
代码语言:javascript复制var dict = new Dictionary<int, string>() { {1,"第一个" },{2,"第二" } };
代码语言:javascript复制添加和输出访问
代码语言:javascript复制 dict.Add( 3,"第一个" );
foreach (var item in dict)
{
Console.WriteLine("Key:" item.Key " Value:" item.Value);
}
代码语言:javascript复制
有序字典SortedDictionary<TKey,TValue>是一个二叉搜索树,其中的元素根据建排序。和前面讲的SortedList<TKey,TValue>的功能类似。但是SortedList<TKey,TValue>是基于数组的列表,而有序字典类为一个字典。多以它们也有不同之处:
- SortedList<TKey,TValue>使用的内存比SortedDictionary<TKey,TValue>少
- SortedDictionary<TKey,TValue>的元素插入和删除比较快
- 在用已排好序的数据填充集合时,若不需要修改容量,SortedList<TKey,TValue>就比较快
六、集
包含不重复元素的的集合称为”集(set)”,.Net Core 包含两个集(HashSet<T>和SortedSet<T>),它们都实现ISet<T>接口,HashSet<T>集包含不重复元素的无序列表,SortedSet<T>集包含不重复元素的有序列表。
ISet<T>接口提供的方法可以创建合集、交集,或者给出一个集是另一个集的超集或子集的信息。
代码语言:javascript复制 static void Main(string[] args)
{
var teams = new HashSet<string>() { "one","two","three"};
var teams1= new HashSet<string>() { "开始","one", "two", "three" };
//Add方法如果集中存在该元素则不添加,返回bool值
if (!teams.Add("one"))
{
Console.WriteLine("one 已经存在集中");
}
//IsSubsetOf方法判断teams集合中的元素是否都包含在teams1中,返回bool值
if (teams.IsSubsetOf(teams1))
{
Console.WriteLine("teams中的值都包含在teams1中");
}
//IsSupersetOf方法判断teams1集合是否是teams集合超集,返回bool值
if (teams1.IsSupersetOf(teams))
{
Console.WriteLine("teams1是teams的超集");
}
//Overlaps方法判断teams集合与teams是否存在重叠的元素,返回bool值
if (teams.Overlaps(teams1))
{
Console.WriteLine("teams与teams1有重叠元素");
}
}
代码语言:javascript复制
总结
许多的集合都提供了相同的功能,例如SortedList类与SortedDictionary类的功能几乎完全相同。但是其性能常常差别非常巨大,一个集合使用的内存少,另一个元素检索起来速度快,在MSDN文档中,集合的方法常常有性能的提示,给出以O记号表示的操作时间:
-
- O(1)
- O(log n)
- O(n)
O(1)表示无论集合中有多少数据项,这个操作需要的时间都不变,例如,ArrayList类的Add()方法就具有这个行为,无论列表有多少个集合,在列表末尾添加一个新元素的时间都相同。
O(n)表示对于集合执行一个操作需要的时间最坏的情况是N,如果需要重新给集合分配内存,ArrayList类的Add()方法就是一个O(n)操作。改变容量,需要复制列表,复制的时间随元素的增加而线性的增加。
O(log n)表示操作需要的时间随集合中元素的增加而增加,但每个元素要增加的时间不是线性的,而是呈对数曲线。在集合中执行插入操作时,SortedDictionary<TKey,TValue>集合类具有O(log n)行为,而SortedList<TKey,TValue>集合具有O(n)行为,这里SortedDictionary<TKey,TValue>集合类就要快的多,因为树形结构中插入元素的效率比列表高的多。
下面表格中则列出了集合类及其执行不同操作的性能。可以使用这个表选择性能最佳的集合类进行使用。
集合 | Add | Insert | Remove | Item | Sort | Find |
---|---|---|---|---|---|---|
List<T> | 如果集合必须重置大小就是O(1)或O(n) | O(n) | O(n) | O(1) | O(n log n)最坏情况O(n^2) | O(n) |
Stack<T>(栈) | Push(),如果栈必须重置大小,就是O(1)或O(n) | no | Pop(),O(1) | no | no | no |
Queue<T>(列队) | Enqueue(),如果栈必须重置大小,就是O(1)或O(n) | no | Dequeu(),O(1) | no | no | no |
HastSet<T>(无序列表) | 如果栈必须重置大小,就是O(1)或O(n) | Add()O(1)或O(n) | O(1) | no | no | no |
LinkedList<T>(链表) | AddLast(),O(1) | AddAfter(),O(1) | O(1) | no | no | O(n) |
-END-