简单的介绍一下集合,通俗来讲就是用来保管多个数据的方案。比如说我们是一个公司的仓库管理,公司有一堆货物需要管理,有同类的,有不同类的,总而言之就是很多、很乱。我们对照集合的概念对仓库进行管理的话,那么 数组就是将一堆货整整齐齐的码在仓库的某个地方,普通列表也是如此;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) { // 依次循环,不需要下标,操作更快一点 }
copyto
复制到
public void copyto(array array, int index); public void copyto(array array, long index);
参数说明: array 需要复制到的数组,index 目标数组的起始下标
方法说明:将 源数组的元素依次复制到 array从index下标开始的位置
string[] strarr1 = new string[]{"1","2","3","4","5","6","7","8","9","10"}; string[] strarr3 = new string[10]; strarr1.copyto(strarr3, 0); //strarr3 = {"1","2","3","4",'5","6","7","8","9","10"}
值得注意的是strarr3
的长度不能 小于 index + strarr1.length
sort
排序
这个方法不是数组对象的方法,而是 array
提供的一个静态方法。
int[] arr1 = new[] {1, 9, 28, 5, 3, 6, 0, 12, 44, 98, 4, 2, 13, 18, 81, 92}; array.sort(arr1);//0,1,2,3,4,5,6,9,12,13,18,28,44,81,92,98
值得注意的是,该方法是直接对数组进行操作,所以不会返回新的数组。
tolist
转成 list
顾名思义,将array对象转成list对象。这里需要额外注意的是,转换成的list是不可改变长度的。
4. clone()
获得一个浅拷贝的数组对象
获取该对象的一个浅拷贝数组对象。
至于其他的array
类和array对象 还有很多有意思的方法,但是平时开发的时候使用的频率比较低。这里就不一一介绍了,以后需要会介绍一下的。
list
列表为一个泛型类,泛型表示<t>,其中t表示列表中存放的元素类型,t代表c#中可实例化的类型。关于泛型的具体描述以后介绍,现在回过头来继续介绍列表。列表内部持有一个数组对象,列表有两个私有变量:一个是列表容量,即内部数组的大小;另一个是存放的元素数量,通过count
获取。
list
列表通过元素数量实现了add
和remove
的操作,列表对象操作引发元素数量变动时都会导致对容量的重新计算,如果现有容量不满足后续操作需要的话,将会对现有数组进行扩充。
list<string> list = new list<string>();// 初始化一个空的列表 list<string> list1 = new list<string>{"12", "2"};//初始化一个包含两个元素的列表 list1 = new list<string>(100);//初始化一个空的列表,并指定list的初始容量为100 list = new list<string>(list1);// 使用一个list/array 初始化一个列表
count
或longcount
获取元素的数量
count 表示获取一个int类型的的数量值,longcount表示获取一个long类型的数量值。通常情况下两者返回的结果是一致的,但是如果列表中元素的数量超过了int允许的最大返回直接使用 count
获取将会出现数据溢出的问题,这时候就需要longcount
了。
访问元素/修改元素
c#的列表操作单个元素很简单 ,与数组的操作方式完全一样。
string str = list1[0];//获取 list1 的第一个元素,即下标为0的元素
list1[2] = "233"; // 将 list1 的第三个元素设置为“233” ,即下标为2 的元素,这里假设list1有至少三个元素
```
需要注意的地方是,如果给定的下标超过了list对象的索引值范围会报argumentoutofrangeexception
。判断方法就是 下标>= count
,如果满足就会越界。
3. add
或addrange
添加到列表最后
将元素添加到list的末尾,`add`添加一个,`addrange`添加一组,支持数组、列表。 ```c# list<string> list = new list<string>();// 初始化一个空的列表 list.add("12");//list = {"12"} list<string> list1 = new list<string>{"14", "2"}; list.addrange(list1);// list = {"12","14","2"} ```
insert(int index, t item)
或insertrange(int index,ienumerable<t> items)
插入
insert(int index,t item)
在 index 下标处插入一个元素,该下标以及该下标以后的元素依次后移insertrange(int index,ienumerable<t> items)
在index下标处插入一组元素,该下标以及之后的元素依次后移示例:
list<int> arr1 = new list<int>{1, 9, 28, 5, 3, 6, 0, 12, 44, 98, 4, 2, 13, 18, 81, 92}; arr1.insert(3,37);// arr1 = 1,9,28,37,5,3,6,0,12,44,98,4,2,13,18,81,92 下标为3的元素变成了37,之后的元素依次后移了
list<int> arr1 = new list<int>{1, 9, 28, 5, 3, 6, 0, 12, 44, 98, 4, 2, 13, 18, 81, 92}; list<int> arr2 = new list<int>{2,3,4,5}; arr1.insertrange(2,arr2);//arr1= 1,9,2,3,4,5,28,5,3,6,0,12,44,98,4,2,13,18,81,92 可以明显发现下标为2的元素发生了变化
contains(t item)
是否包含
返回一个boolean类型的结果,如果包含则返回true
,如果不包含则返回false
list<int> arr2 = new list<int>{2,3,4,5}; arr2.contains(8);//false arr2.contains(3);//true
remove(t item)
删除指定元素
list<int> arr2 = new list<int>{2,3,4,5}; arr2.remove(3);// arr2 = 2,4,5 arr2.remove(6);//arr2 = 2,4,5
值得注意的是,如果删除一个不存在的元素时,不会报错,列表也不会发生任何改变。
removeat(int index)
删除位于下标的元素
list<int> arr2 = new list<int>{2,3,4,5}; arr2.removeat(1);//arr2 = 2,4,5
如果移除的下标超过了列表的最后一个元素的下标将会抛出异常
removerane(ienumerable<t> items)
删除一组元素
与remove(t item)
一致,如果要删除的元素不在列表中,则列表元素不会发生变化。
list<int> arr1 = new list<int>{1, 9, 28, 5, 3, 6, 0, 12, 44, 98, 4, 2, 13, 18, 81, 92}; list<int> arr2 = new list<int>{2,3,4,5}; arr1.removerange(arr2);
getrange(int index,int count)
从列表中获取一个子列表,从index
开始,获取count
个元素,如果源列表中从index
开始剩余的元素不足count
个将会报错。
clear()
删除所有元素
将列表清空,调用方法之后,列表中将不包含任何元素
reverse()
调转顺序
将列表按照从尾到头的顺序进行排列
indexof(t item)
查找下标
查找元素在列表中的下标,如果没找到元素,则返回-1
sort()
排序
对列表进行排序,调用方法后,会按照默认排序方法返回一个排序结果
c#没有为set
单独设置类,一方面是因为set出镜率不高,另一方面也因为set
本身的机制所致。set集合不能包含重复元素,如果尝试存入重复元素集合元素将不会发生任何变化。
set集合中元素的顺序与存放顺序不一定相同。因为set集合中存放对于使用者而言是乱序存放的。
我们常用的set集合有 hashset<t>
和sortset<t>
,其他的set相关类则属于更加少见。至少在我5年多的开发经历中没有用过。
hashset<t>
和sortset<t>
hashset
俗称 哈希集合或者哈希set,内部使用hash值作为元素的唯一性验证,即调用对象的hashcode()
方法作为hash值的来源。sortset
顾名思义,排序集合,它每次在插入的时候都会对元素进行一次排序初始化
两者相同的地方就是 都有以下几种初始化方法
set<t> set = new hashset<t>();// = new sortset<t>(); 初始化一个空的集合 //使用一个集合对象初始化 set<t> set1 = new hashset<t>(ienumerable<t> items);// = new sortset<t>(ienumerable<t> items); set<t> set2 = new hashset<t>(){t t1, t t2, t t3};// 与上一种一样
添加元素
set1.add(item);// 集合只支持添加单个元素,但是可以通过集合运算的方式增加多个元素
移除元素
set1.remove(item);//删除集合中与item判断相等的元素
访问元素
需要注意的地方是,c#对set没有支持下标访问方式获取set里的元素,这是因为索引位置对于集合来说意义不大,没有操作意义。
foreach (var item in set1) { // 操作 }
set 只能通过遍历访问元素,不能通过get或者下标操作访问元素。关于foreach
循环会在下一篇《c#基础知识
系列》里进行介绍。
集合运算
unionwith
并
sortedset<int> set = new sortedset<int>{1,0,29,38,33,48,17}; set.unionwith(new []{5,57,8,4,3,1,0,33}); // set = 0,1,3,4,5,8,17,29,33,38,48,57
通过传入一个集合对象,将该集合设置为两个集合的并集,也就是说取上图 a,b,c 三个区域的和
exceptwith
差
sortedset<int> set = new sortedset<int>{1,0,29,38,33,48,17}; set.exceptwith(new []{5,57,8,4,3,1,0,33}); // set =17,29,38,48
传入一个集合,从set中去掉同属于两个集合的元素,保留只存在于set的元素,也就是取上图中的a部分元素
intersectwith
交
sortedset<int> set = new sortedset<int>{1,0,29,38,33,48,17}; set.exceptwith(new []{5,57,8,4,3,1,0,33}); // set =0,1,33
传入一个集合,保留set与传入集合里相同的元素,也就是说取的是上图中的b部分
symmetricexceptwith
余集
sortedset<int> set = new sortedset<int>{1,0,29,38,33,48,17}; set.symmetricexceptwith(new []{5,57,8,4,3,1,0,33});//set= 3,4,5,8,17,29,38,48,57
传入一个集合,保留set与传入集合两个集合中不同的元素,也就是取上图的a+c这两部分。
contains
包含
判断集合中是否包含目标元素,返回true/false
sortedset<int> set = new sortedset<int>{1,0,29,38,33,48,17}; set.contains(1);// true
hashset<t>
支持传入一个自定义的相等比较器,该比较器需要返回一个 bool值;可以指定起始容量sortset<t>
支持传入一个自定义的大小比较器,该比较器返回一个int值;不能指定起始容量comparer
属性:sortset 可以获取大小比较器;hashset 获取一个相等比较器dictionary
字典,正如它的名称一样,dictionary
需要指定两个类型,一个作为索引键,一个作为数据值。就像字典一样,每一个词条内容都只有一个字词索引,但可以出现同义词一样。当然,作为我博大精深的中文会出现同字不同音的词组,但是一旦把音、字组合起来作为索引,那还是只会出现一个词条。
所以 dictionary
的使用方式也跟字典一样,通过索引访问和操作数据。
dictionary
的初始化有如下几个方法:
dictionary<string, int> dict = new dictionary<string, int>();// 键是字符串,值是int类型 dictionary<string,int> dict1 = new dictionary<string, int>(10);// 指定初始容量是10 dictionary<string,int> dict2 = new dictionary<string, int>() { {"1",1}, {"2",2} };// 在大括号标记中 通过 {key,value}的写法创建一个 字典对象,并包含这些键值对 // 传入一个字典对象,以传入的对象为基础创建一个字典 dictionary<string,int> dict3 = new dictionary<string, int>(dict2);
添加元素
dictionary<string, int> dict = new dictionary<string, int>(); // 方法一 dict.add("1",2);//添加一个 键为“1”,值为2的键值对。 //方法二 //字典可以类似列表的形式通过下标添加或更新键对应的值, //不过与列表不同的是,字典的下标是字符串 dict["2"] = 4;// 如果 dict中2有值,则更新为4,如果没有,则设置2对应的值为4
获取元素
dictionary<string, int> dict = new dictionary<string, int>(); /* 省略数据填充阶段 */ int value = dict["2"]; // value = 4 // 如果dictionary中不存在索引为“2”的数据 // 将会抛出 system.collections.generic.keynotfoundexception 异常
c# 的dictionary
还有一个trygetvalue
方法可以用来尝试获取,他的使用方法是这样的:
int obj = 0; boolean iscontains = dict.trygetvalue("3", out obj); // 方法会返回 dict是否包含键“3”的结果,如果有 obj 则存放了dict中对应的值,如果没有,则返回false且不改变 obj 的值
count
获取dictionary
里键值对的数量。
int count = dict.count;
dictionary没有longcount
属性,因为对于dictionary
存放数据需要比对key
的相等性,如果存放巨量数据将会对数据的访问和操作效率有影响。
keys
获取dictionary
里所有的键,返回一个keycollection对象,不需要关心这是一个什么类型,可以简单的把它当做一个存放了键的hashset
。
containskey()
是否包含键:通常与获取元素一起使用,可以先判断dictionary
里是否有这个键,然后再进行后续操作。
remove()
删除dictionary
中键对应的元素,删除后再次访问会报错。如果删除一个不存在的元素将返回flase。
操作示例:
dictionary<string,int> dict = new dictionary<string, int>(); //省略赋值操作 bool result = dict.remove("2");// 如果dict里包含键为“2”的元素,则result为true,否则为false
另一种方法:
int value = 0; bool result = dict.remove("2", out value); // 如果dict 里包含键为“2”的元素,则result 为 false且value为对应的值
containsvalue()
是否包含值,与containskey
的用法一样,只不过遍历的是值;用处不大。
values
获取值的集合类似与keyvalues
。
c#的传统集合基本都存放在system.collections
命名空间里,详细的可以查看。这个命名空间里的集合类使用都不多,不过c#的集合体系的接口规范都是在这个里面定义的。
arraylist
list的非泛型版,与list操作方法一致,不过返回值是object类型
sortedlist
一个排序的键值对集合,我没用过,不过官方给了如下示例:
using system; using system.collections; public class samplessortedlist { public static void main() { // creates and initializes a new sortedlist. sortedlist mysl = new sortedlist(); mysl.add("third", "!"); mysl.add("second", "world"); mysl.add("first", "hello"); // displays the properties and values of the sortedlist. console.writeline( "mysl" ); console.writeline( " count: {0}", mysl.count ); console.writeline( " capacity: {0}", mysl.capacity ); console.writeline( " keys and values:" ); printkeysandvalues( mysl ); } public static void printkeysandvalues( sortedlist mylist ) { console.writeline( "\t-key-\t-value-" ); for ( int i = 0; i < mylist.count; i++ ) { console.writeline( "\t{0}:\t{1}", mylist.getkey(i), mylist.getbyindex(i) ); } console.writeline(); } }
hashtable
表示根据键的哈希代码进行组织的键/值对的集合。hashtable
的结构类似于dictionary但又与其不同,它的键值存储用的是hash值。以下是官方给出的示例代码:
using system; using system.collections; class example { public static void main() { // create a new hash table. // hashtable openwith = new hashtable(); // add some elements to the hash table. there are no // duplicate keys, but some of the values are duplicates. openwith.add("txt", "notepad.exe"); openwith.add("bmp", "paint.exe"); openwith.add("dib", "paint.exe"); openwith.add("rtf", "wordpad.exe"); // the add method throws an exception if the new key is // already in the hash table. try { openwith.add("txt", "winword.exe"); } catch { console.writeline("an element with key = \"txt\" already exists."); } // the item property is the default property, so you // can omit its name when accessing elements. console.writeline("for key = \"rtf\", value = {0}.", openwith["rtf"]); // the default item property can be used to change the value // associated with a key. openwith["rtf"] = "winword.exe"; console.writeline("for key = \"rtf\", value = {0}.", openwith["rtf"]); // if a key does not exist, setting the default item property // for that key adds a new key/value pair. openwith["doc"] = "winword.exe"; // containskey can be used to test keys before inserting // them. if (!openwith.containskey("ht")) { openwith.add("ht", "hypertrm.exe"); console.writeline("value added for key = \"ht\": {0}", openwith["ht"]); } // when you use foreach to enumerate hash table elements, // the elements are retrieved as keyvaluepair objects. console.writeline(); foreach( dictionaryentry de in openwith ) { console.writeline("key = {0}, value = {1}", de.key, de.value); } // to get the values alone, use the values property. icollection valuecoll = openwith.values; // the elements of the valuecollection are strongly typed // with the type that was specified for hash table values. console.writeline(); foreach( string s in valuecoll ) { console.writeline("value = {0}", s); } // to get the keys alone, use the keys property. icollection keycoll = openwith.keys; // the elements of the keycollection are strongly typed // with the type that was specified for hash table keys. console.writeline(); foreach( string s in keycoll ) { console.writeline("key = {0}", s); } // use the remove method to remove a key/value pair. console.writeline("\nremove(\"doc\")"); openwith.remove("doc"); if (!openwith.containskey("doc")) { console.writeline("key \"doc\" is not found."); } } } /* this code example produces the following output: an element with key = "txt" already exists. for key = "rtf", value = wordpad.exe. for key = "rtf", value = winword.exe. value added for key = "ht": hypertrm.exe key = dib, value = paint.exe key = txt, value = notepad.exe key = ht, value = hypertrm.exe key = bmp, value = paint.exe key = rtf, value = winword.exe key = doc, value = winword.exe value = paint.exe value = notepad.exe value = hypertrm.exe value = paint.exe value = winword.exe value = winword.exe key = dib key = txt key = ht key = bmp key = rtf key = doc remove("doc") key "doc" is not found. */
虽然c#框架保留了非泛型集合元素,但不建议使用非泛型集合进行开发。
除了之前所说的几个集合类,c#还设置了一些在开发中不常用但在特定场合很有用的集合类。
queue<t>
和 queue
这两个类是一对的,一个是泛型类,一个是非泛型类。该类中文名称是队列,如其名,队列讲究一个先进先出,所以队列每次取元素都是从头取,存放是放到队列尾。
操作代码如下:
加入队列
queue queue = new queue(); queue.enqueue(1); queue.enqueue("2"); queue<string> queue1 = new queue<string>(); queue1.enqueue("stri");//
读取队首的元素
读取有两种:
读取但不移除元素:
object obj= queue.peek(); string str = queue.peek();
读取并移除元素:
object obj = queue.dequeue(); string str = queue.dequeue();
linkedlist<t>
linkedlist
,链表。与list不同的地方是,linkedlist
的元素是linkedlistnode
对象,该对象有四个属性,分别是list
-指向列表对象,previous
指向前一个对象如果有的话,next
指向后一个对象如果有的话。所以根据元素的属性可以发现链表的工作方式,链表就像一条锁链一样,一个元素分三块,一个指向前一个元素,一个用来存放值,一个指向下一个元素,简单如下图所示:
所以可以明显的发现linkedlist
在随机插取上比一般的要快,因为它不用维护一个数组,但是在查找和坐标操作上明显要慢很多。
linkedlist
简单介绍这么多,可以看看它的一些常见操作:
first
第一个元素
获取第一个元素
last
最后一个元素
获取最后一个元素
addafter
/addbefore
在某个节点后/在某个节点前插入数据
支持以下参数列表:
第一个参数表示要插入的节点位置,第二个表示要插入的节点/元素。第一个参数会校验是否属于该链表,如果不属于则会抛出一个异常。第二个可以是值,也可以是初始化好的节点对象。如果是节点对象,则判断是否归属其他链表,如果是其他链表抛出异常。
addfirst
/addlast
添加元素到头或者尾,可以使用linkedlistnode
或者添加值。
remove
删除,可以传递某个节点,或者要删除的节点里存放的值。
removefirst
/removelast
删除第一个节点,删除最后一个节点,不含参数
下面是微软官方的一些示例
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<string> sentence = new linkedlist<string>(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<string> 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<string> 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<string> 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<string> 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<string> words, string test) { console.writeline(test); foreach (string word in words) { console.write(word + " "); } console.writeline(); console.writeline(); } private static void indicatenode(linkedlistnode<string> node, string test) { console.writeline(test); if (node.list == null) { console.writeline("node '{0}' is not in the list.\n", node.value); return; } stringbuilder result = new stringbuilder("(" + node.value + ")"); linkedlistnode<string> 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<t>
和 stack
stack
广泛的翻译是栈,是一种后进先出的集合。在一些特殊场景里,使用十分广泛。
stack
有两个很重要的方法pop
和push
,出/进。pop 获取最后一个元素,并退出栈,push 向栈推入一个元素。
具体可以参照
c# 的集合还有其他的一些命名空间里藏着宝贝,不过在实际开发中使用频率并不大,可以按需查看。
system.collections.concurrent
线程安全这个命名空间,提供了一系列线程安全的集合类,当出现多线程操作集合的时候,应当使用这个命名空间的集合。名称和常用的类是一一对应的,不过只提供了concurrentdictionary<tkey,tvalue>
、concurrentqueue<t>
、concurrentstack<t>
等几个集合类。具体可以查看
system.collections.immutable
不可变集合命名空间包含用于定义不可变集合的接口和类,如果需要使用这个命名空间,则需要使用nuget下载。
更多内容烦请关注
如对本文有疑问, 点击进行留言回复!!
使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)
C#实现获取本地内网(局域网)和外网(公网)IP地址的方法分析
浅谈Visual Studio 2019 Vue项目的目录结构
网友评论