简单的介绍一下集合,通俗来讲就是用来保管多个数据的方案。比如说我们是一个公司的仓库管理,公司有一堆货物需要管理,有同类的,有不同类的,总而言之就是很多、很乱。我们对照集合的概念对仓库进行管理的话,那么 数组就是将一堆货整整齐齐的码在仓库的某个地方,普通列表也是如此;Set就是在仓库里有这么一个货架,每种货品只能放一个,一旦某种货品超过一个了货架就塌了;Dictionary字典呢,在一个货架上随机摆放,然后再找一个本子把每个货品存放的位置记录下来。
C#/.NET Framework 提供了很多很有意思的集合类,数组、列表、链表、Set、字典等一系列的类。其中数组是语言的一部分,个人认为严格意义上不属于集合类这一部分。C#开发中常用的集合有数组、 List类、Set接口、Dictionary类、Queue类、LinkedList类等,其他的出镜率不高。 与其他(java)语言不同的一点是,C#的List是类,而不是接口,接口是IList,但这个接口意义不大,在使用IList的时候更多的倾向于使用IEnumerable,这主要是因为IEnumerable 有 Linq的支持再者两者的方法基本一致,能用IList的地方基本都可以用IEnumerable。
数组,集合的基础部分,主要特点是一经初始化就无法再次对数组本身进行增删元素。C#虽然添加了一些修改数组的扩展方法,但基本都会返回新的数组对象。
数组的初始化需要指定大小,可以显示指定或者隐式的指定。
// 显示指定类型与大小,具体的元素后续赋值string[] strArr = new string[10]; //指定类型同时给元素赋值,具体大小由编译器自动推断string[] strArr1 = new string[]{"1","2","3","4","5","6","7","8","9","10"};// 类型和大小都由编译器进行推断string[] strArr2 = new []{"1","2","3","4","5","6","7","8","9","10"};
string item0 = strArr[0]; //取出 "1"string item2 = strArr[2]; // 取出 "3"strArr[0] = "3"; // strArr = {"3","2","3","4","5","6","7","8","9","10"}
int length = strArr.Length;// 获取一个整型的长度//获取一个长整型的长度,对于一个非常大的数组且长度可能会超过int的最大值long longLength = strArr.LongLength;
// 普通for 循环for(int i = 0;i < strArr.Length;i++){ string it = strArr[i];}// foreach 循环foreach(string it in strArr){ // 依次循环,不需要下标,操作更快一点}
至于其他的Array类和Array对象 还有很多有意思的方法,但是平时开发的时候使用的频率比较低。这里就不一一介绍了,以后需要会介绍一下的。
List列表为一个泛型类,泛型表示,其中T表示列表中存放的元素类型,T代表C#中可实例化的类型。关于泛型的具体描述以后介绍,现在回过头来继续介绍列表。列表内部持有一个数组对象,列表有两个私有变量:一个是列表容量,即内部数组的大小;另一个是存放的元素数量,通过Count获取。 List列表通过元素数量实现了Add和Remove 的操作,列表对象操作引发元素数量变动时都会导致对容量的重新计算,如果现有容量不满足后续操作需要的话,将会对现有数组进行扩充。
List list = new List();// 初始化一个空的列表List list1 = new List{"12", "2"};//初始化一个包含两个元素的列表list1 = new List(100);//初始化一个空的列表,并指定list的初始容量为100list = new List(list1);// 使用一个List/Array 初始化一个列表
C#没有为Set单独设置类,一方面是因为Set出镜率不高,另一方面也因为Set本身的机制所致。Set集合不能包含重复元素,如果尝试存入重复元素集合元素将不会发生任何变化。 Set集合中元素的顺序与存放顺序不一定相同。因为Set集合中存放对于使用者而言是乱序存放的。 我们常用的Set集合有 HashSet和SortSet,其他的Set相关类则属于更加少见。至少在我5年多的开发经历中没有用过。
Dictionary 字典,正如它的名称一样,Dictionary 需要指定两个类型,一个作为索引键,一个作为数据值。就像字典一样,每一个词条内容都只有一个字词索引,但可以出现同义词一样。当然,作为我博大精深的中文会出现同字不同音的词组,但是一旦把音、字组合起来作为索引,那还是只会出现一个词条。 所以 Dictionary的使用方式也跟字典一样,通过索引访问和操作数据。
Dictionary的初始化有如下几个方法:
Dictionary dict = new Dictionary();// 键是字符串,值是int类型Dictionary dict1 = new Dictionary(10);// 指定初始容量是10Dictionary dict2 = new Dictionary(){ {"1",1}, {"2",2}};// 在大括号标记中 通过 {key,value}的写法创建一个 字典对象,并包含这些键值对// 传入一个字典对象,以传入的对象为基础创建一个字典Dictionary dict3 = new Dictionary(dict2);
C#的传统集合基本都存放在System.Collections命名空间里,详细的可以查看微软官方文档。这个命名空间里的集合类使用都不多,不过C#的集合体系的接口规范都是在这个里面定义的。
除了之前所说的几个集合类,C#还设置了一些在开发中不常用但在特定场合很有用的集合类。
这两个类是一对的,一个是泛型类,一个是非泛型类。该类中文名称是队列,如其名,队列讲究一个先进先出,所以队列每次取元素都是从头取,存放是放到队列尾。 操作代码如下:
LinkedList,链表。与List不同的地方是,LinkedList的元素是LinkedListNode对象,该对象有四个属性,分别是List -指向列表对象,Previous指向前一个对象如果有的话,Next指向后一个对象如果有的话。所以根据元素的属性可以发现链表的工作方式,链表就像一条锁链一样,一个元素分三块,一个指向前一个元素,一个用来存放值,一个指向下一个元素,简单如下图所示:
所以可以明显的发现LinkedList在随机插取上比一般的要快,因为它不用维护一个数组,但是在查找和坐标操作上明显要慢很多。 LinkedList简单介绍这么多,可以看看它的一些常见操作:
下面是微软官方的一些示例
using System;using System.Text;using System.Collections.Generic;public class Example{ public static void Main() { // Create the link list. string[] words = { "the", "fox", "jumps", "over", "the", "dog" }; LinkedList sentence = new LinkedList(words); Display(sentence, "The linked list values:"); Console.WriteLine("sentence.Contains("jumps") = {0}", sentence.Contains("jumps")); // Add the word 'today' to the beginning of the linked list. sentence.AddFirst("today"); Display(sentence, "Test 1: Add 'today' to beginning of the list:"); // Move the first node to be the last node. LinkedListNode mark1 = sentence.First; sentence.RemoveFirst(); sentence.AddLast(mark1); Display(sentence, "Test 2: Move first node to be last node:"); // Change the last node to 'yesterday'. sentence.RemoveLast(); sentence.AddLast("yesterday"); Display(sentence, "Test 3: Change the last node to 'yesterday':"); // Move the last node to be the first node. mark1 = sentence.Last; sentence.RemoveLast(); sentence.AddFirst(mark1); Display(sentence, "Test 4: Move last node to be first node:"); // Indicate the last occurence of 'the'. sentence.RemoveFirst(); LinkedListNode current = sentence.FindLast("the"); IndicateNode(current, "Test 5: Indicate last occurence of 'the':"); // Add 'lazy' and 'old' after 'the' (the LinkedListNode named current). sentence.AddAfter(current, "old"); sentence.AddAfter(current, "lazy"); IndicateNode(current, "Test 6: Add 'lazy' and 'old' after 'the':"); // Indicate 'fox' node. current = sentence.Find("fox"); IndicateNode(current, "Test 7: Indicate the 'fox' node:"); // Add 'quick' and 'brown' before 'fox': sentence.AddBefore(current, "quick"); sentence.AddBefore(current, "brown"); IndicateNode(current, "Test 8: Add 'quick' and 'brown' before 'fox':"); // Keep a reference to the current node, 'fox', // and to the previous node in the list. Indicate the 'dog' node. mark1 = current; LinkedListNode mark2 = current.Previous; current = sentence.Find("dog"); IndicateNode(current, "Test 9: Indicate the 'dog' node:"); // The AddBefore method throws an InvalidOperationException // if you try to add a node that already belongs to a list. Console.WriteLine("Test 10: Throw exception by adding node (fox) already in the list:"); try { sentence.AddBefore(current, mark1); } catch (InvalidOperationException ex) { Console.WriteLine("Exception message: {0}", ex.Message); } Console.WriteLine(); // Remove the node referred to by mark1, and then add it // before the node referred to by current. // Indicate the node referred to by current. sentence.Remove(mark1); sentence.AddBefore(current, mark1); IndicateNode(current, "Test 11: Move a referenced node (fox) before the current node (dog):"); // Remove the node referred to by current. sentence.Remove(current); IndicateNode(current, "Test 12: Remove current node (dog) and attempt to indicate it:"); // Add the node after the node referred to by mark2. sentence.AddAfter(mark2, current); IndicateNode(current, "Test 13: Add node removed in test 11 after a referenced node (brown):"); // The Remove method finds and removes the // first node that that has the specified value. sentence.Remove("old"); Display(sentence, "Test 14: Remove node that has the value 'old':"); // When the linked list is cast to ICollection(Of String), // the Add method adds a node to the end of the list. sentence.RemoveLast(); ICollection icoll = sentence; icoll.Add("rhinoceros"); Display(sentence, "Test 15: Remove last node, cast to ICollection, and add 'rhinoceros':"); Console.WriteLine("Test 16: Copy the list to an array:"); // Create an array with the same number of // elements as the inked list. string[] sArray = new string[sentence.Count]; sentence.CopyTo(sArray, 0); foreach (string s in sArray) { Console.WriteLine(s); } // Release all the nodes. sentence.Clear(); Console.WriteLine(); Console.WriteLine("Test 17: Clear linked list. Contains 'jumps' = {0}", sentence.Contains("jumps")); Console.ReadLine(); } private static void Display(LinkedList words, string test) { Console.WriteLine(test); foreach (string word in words) { Console.Write(word + " "); } Console.WriteLine(); Console.WriteLine(); } private static void IndicateNode(LinkedListNode node, string test) { Console.WriteLine(test); if (node.List == null) { Console.WriteLine("Node '{0}' is not in the list.", node.Value); return; } StringBuilder result = new StringBuilder("(" + node.Value + ")"); LinkedListNode nodeP = node.Previous; while (nodeP != null) { result.Insert(0, nodeP.Value + " "); nodeP = nodeP.Previous; } node = node.Next; while (node != null) { result.Append(" " + node.Value); node = node.Next; } Console.WriteLine(result); Console.WriteLine(); }}//This code example produces the following output:The linked list values://the fox jumps over the dog//Test 1: Add 'today' to beginning of the list://today the fox jumps over the dog//Test 2: Move first node to be last node://the fox jumps over the dog today//Test 3: Change the last node to 'yesterday'://the fox jumps over the dog yesterday//Test 4: Move last node to be first node://yesterday the fox jumps over the dog//Test 5: Indicate last occurence of 'the'://the fox jumps over (the) dog//Test 6: Add 'lazy' and 'old' after 'the'://the fox jumps over (the) lazy old dog//Test 7: Indicate the 'fox' node://the (fox) jumps over the lazy old dog//Test 8: Add 'quick' and 'brown' before 'fox'://the quick brown (fox) jumps over the lazy old dog//Test 9: Indicate the 'dog' node://the quick brown fox jumps over the lazy old (dog)//Test 10: Throw exception by adding node (fox) already in the list://Exception message: The LinkedList node belongs a LinkedList.//Test 11: Move a referenced node (fox) before the current node (dog)://the quick brown jumps over the lazy old fox (dog)//Test 12: Remove current node (dog) and attempt to indicate it://Node 'dog' is not in the list.//Test 13: Add node removed in test 11 after a referenced node (brown)://the quick brown (dog) jumps over the lazy old fox//Test 14: Remove node that has the value 'old'://the quick brown dog jumps over the lazy fox//Test 15: Remove last node, cast to ICollection, and add 'rhinoceros'://the quick brown dog jumps over the lazy rhinoceros//Test 16: Copy the list to an array://the//quick//brown//dog//jumps//over//the//lazy//rhinoceros//Test 17: Clear linked list. Contains 'jumps' = False//
Stack广泛的翻译是栈,是一种后进先出的集合。在一些特殊场景里,使用十分广泛。 Stack有两个很重要的方法Pop 和Push,出/进。Pop 获取最后一个元素,并退出栈,Push 向栈推入一个元素。 具体可以参照官方文档
C# 的集合还有其他的一些命名空间里藏着宝贝,不过在实际开发中使用频率并不大,可以按需查看。
这个命名空间,提供了一系列线程安全的集合类,当出现多线程操作集合的时候,应当使用这个命名空间的集合。名称和常用的类是一一对应的,不过只提供了ConcurrentDictionary、ConcurrentQueue、ConcurrentStack等几个集合类。具体可以查看官方文档
命名空间包含用于定义不可变集合的接口和类,如果需要使用这个命名空间,则需要使用NuGet下载。