1.几种常见的数据结构
这里主要总结下小匹夫在工作中常碰到的几种数据结构:Array,ArrayList,List<T>,LinkedList<T>,Queue<T>,Stack<T>,Dictionary<K,T>
数组Array:
数组是最简单的数据结构。其具有如下特点:
-
数组存储在连续的内存上。
-
数组的内容都是相同类型。
-
数组可以直接通过下标访问。
数组Array的创建:
int size = 5;
int[] test = new int[size];
创建一个新的数组时将在 CLR 托管堆中分配一块连续的内存空间,来盛放数量为size,类型为所声明类型的数组元素。如果类型为值类型,则将会有size个未装箱的该类型的值被创建。如果类型为引用类型,则将会有size个相应类型的引用被创建。
由于是在连续内存上存储的,所以它的索引速度非常快,访问一个元素的时间是恒定的也就是说与数组的元素数量无关,而且赋值与修改元素也很简单。
string[] test2 = new string[3];
//赋值
test2[0] = "chen";
test2[1] = "j";
test2[2] = "d";
//修改
test2[0] = "chenjd";
但是有优点,那么就一定会伴随着缺点。由于是连续存储,所以在两个元素之间插入新的元素就变得不方便。而且就像上面的代码所显示的那样,声明一个新的数组时,必须指定其长度,这就会存在一个潜在的问题,那就是当我们声明的长度过长时,显然会浪费内存,当我们声明长度过短的时候,则面临这溢出的风险。这就使得写代码像是投机,小匹夫很厌恶这样的行为!针对这种缺点,下面隆重推出ArrayList。
ArrayList:
为了解决数组创建时必须指定长度以及只能存放相同类型的缺点而推出的数据结构。ArrayList是System.Collections命名空间下的一 部分,所以若要使用则必须引入System.Collections。正如上文所说,ArrayList解决了数组的一些缺点。
-
不必在声明ArrayList时指定它的长度,这是由于ArrayList对象的长度是按照其中存储的数据来动态增长与缩减的。
-
ArrayList可以存储不同类型的元素。这是由于ArrayList会把它的元素都当做Object来处理。因而,加入不同类型的元素是允许的。
ArrayList的操作:
ArrayList test3 = new ArrayList();
//新增数据
test3.Add("chen");
test3.Add("j");
test3.Add("d");
test3.Add("is");
test3.Add(25);
//修改数据
test3[4] = 26;
//删除数据
test3.RemoveAt(4);
说了那么一堆”优点“,也该说说缺点了吧。为什么要给”优点”打上引号呢?那是因为ArrayList可以存储不同类型数据的原因是由于把所有的类型都当做Object来做处理,也就是说ArrayList的元素其实都是Object类型的,辣么问题就来了。
-
ArrayList不是类型安全的。因为把不同的类型都当做Object来做处理,很有可能会在使用ArrayList时发生类型不匹配的情况。
-
如上文所诉,数组存储值类型时并未发生装箱,但是ArrayList由于把所有类型都当做了Object,所以不可避免的当插入值类型时会发生装箱操作,在索引取值时会发生拆箱操作。这能忍吗?
注:为何说频繁的没有必要的装箱和拆箱不能忍呢?且听小匹夫慢慢道来:所谓装箱 (boxing):就是值类型实例到对象的转换(百度百科)。那么拆箱:就是将引用类型转换为值类型咯(还是来自百度百科)。下面举个栗子~
//装箱,将String类型的值FanyoyChenjd赋值给对象。
String info = ”FanyoyChenjd”;
object obj=(object)info;
//拆箱,从Obj中提取值给info
object obj = "FanyoyChenjd";
String info = (String)obj;
那么结论呢?好吧,请允许小匹夫很low再次引用百度百科。显然,从原理上可以看出,装箱时,生成的是全新的引用对象,这会有时间损耗,也就是造成效率降低。
List<T>泛型List
为了解决ArrayList不安全类型与装箱拆箱的缺点,所以出现了泛型的概念,作为一种新的数组类型引入。也是工作中经常用到的数组类型。和 ArrayList很相似,长度都可以灵活的改变,最大的不同在于在声明List集合时,我们同时需要为其声明List集合内数据的对象类型,这点又和 Array很相似,其实List<T>内部使用了Array来实现。
List<string> test4 = new List<string>();
//新增数据
test4.Add(“Fanyoy”);
test4.Add(“Chenjd”);
//修改数据
test4[1] = “murongxiaopifu”;
//移除数据
test4.RemoveAt(0);
这么做最大的好处就是
-
即确保了类型安全。
-
也取消了装箱和拆箱的操作。
-
它融合了Array可以快速访问的优点以及ArrayList长度可以灵活变化的优点。
假设各位和小匹夫一样,在工作中最常使用的一种数据结构就是它。那么我们是否能再多一点好奇心呢?那就是探究一下,如果我们自己实现一个类似的数据结构,该从何处下手呢?
下面小匹夫就抛砖引玉了。
刚才说过了,List<T>的内部其实也是一个Array,且是强类型的,所以我们的简单实现(暂且称之为 EggArray<T>)也秉承这个特点,内部通过一个Array来实现,且需要声明类型。但是同时我们也看到List<T>继 承和实现了很多接口,比如IEnumerable接口等,而且值类型和引用类型通吃。这里为了EggArray<T>实现起来轻装简行,我们 不继承List<T>继承的各种接口,同时我们的EggArray只服务于引用类型。
那么首先明确了,它是一个处理引用类型,且实现了泛型的。那么定义就出来了:
//EggArray类
//定义
public class EggArray<T> where T : class
{
}
那么下一步呢?该确定它的内部成员了,就先从字段和属性开始吧。
属性&变量
//EggArray<T>的属性&&变量
private int capacity;
private int count;
private T[] items;
public int Count
{
get
{
return this.count;
}
}
public int Capacity
{
get
{
return this.capacity;
}
}
之后呢?好像是需要一个构造函数了。上文也说了,貌似new的时候不需要指定容量呀。那么我们就把构造函数做成这样吧。
构造函数:
//EggArray的构造函数,默认容量为8
public EggArray() : this(8)
{
}
public EggArray(int capacity)
{
this.capacity = capacity;
this.items = new T[capacity];
}
好了,构造函数也说完了,那么就介绍一下私有方法,因为运行机制全部是有私有方法来运筹的,公共方法只不过是开放给我们的使用的罢了。小匹夫对公共方法的实现没有兴趣,这里就不做演示了。
刚刚也说了,List<T>是无所谓初始长度的,可以用Add()方法往里面添加元素,同时也不可能是有一个无限大的空间让它来存储,那么究 竟它究竟为何能做到这一点呢?因为有一个能动态调整内部数组大小的方法存在,且调整大小是按照原有长度成倍增长的。我们姑且称之为Resize。
那么在进行下面的内容之前,小匹夫还想先问各位一个问题:
List<int> test = new List<int>(){0,1,2,3,4,5,6,7,8,9};
int count = 0;
for(int i = 0; i < test.Count; i++)
{
if(i == 1)
test.Remove(test[i]);
count++;
}
Debug.Log (count);
上面这段代码会输出什么呢?答案是9。可能有的盆油会感到奇怪,test进去时长度明明是10啊。就算你中间Remove了一个元素,可为什么会影响后面 的元素呢?(比如把index为1的元素remove掉,原来index为2的元素现在的index就成1了。)感觉乱套有木有?其实这里 List<T>在执行remove的同时,也把内部的数组压缩了。所以也肯定有一个方法用来压缩咯。我们姑且称为Compact。
私有方法
//当数组元素个[/size][/backcolor][/color][i][color=White][backcolor=DarkGreen][size=2]数不小于数组容量时,需要扩容,增长因子growthFactor为2
private void Resize()
{
int capacity = this.capacity * growthFactor;
if (this.count > capacity)
{
this.count = capacity;
}
T[] destinationArray = new T[capacity];
Array.Copy(this.items, destinationArray, this.count);
this.items = destinationArray;
this.capacity = capacity;
}
private void Compact()
{
int num = 0;
for (int i = 0; i < this.count; i++)
{
if (this.items[i] == null)
{
num++;
}
else if (num > 0)
{
this.items[i - num] = this.items[i];
this.items[i] = null;
}
}
this.count -= num;
}[i][i][i]
LinkedList<T>
也就是链表了。和上述的数组最大的不同之处就是在于链表在内存存储的排序上可能是不连续的。这是由于链表是通过上一个元素指向下一个元素来排列的,所以可能不能通过下标来访问。如图
既然链表最大的特点就是存储在内存的空间不一定连续,那么链表相对于数组最大优势和劣势就显而易见了。
-
向链表中插入或删除节点无需调整结构的容量。因为本身不是连续存储而是靠各对象的指针所决定,所以添加元素和删除元素都要比数组要有优势。
-
链表适合在需要有序的排序的情境下增加新的元素,这里还拿数组做对比,例如要在数组中间某个位置增加新的元素,则可能需要移动移动很多元素,而对于链表而言可能只是若干元素的指向发生变化而已。
-
有优点就有缺点,由于其在内存空间中不一定是连续排列,所以访问时候无法利用下标,而是必须从头结点开始,逐次遍历下一个节点直到寻找到目标。所以当需要快速访问对象时,数组无疑更有优势。
综上,链表适合元素数量不固定,需要两端存取且经常增减节点的情况。
关于链表的使用,MSDN上有详细的例子。
Queue<T>
在Queue<T>这种数据结构中,最先插入在元素将是最先被删除;反之最后插入的元素将最后被删除,因此队列又称为“先进先出”(FIFO —first in first out)的线性表。通过使用Enqueue和Dequeue这两个方法来实现对 Queue<T> 的存取。
一些需要注意的地方:
-
先进先出的情景。
-
默认情况下,Queue<T>的初始容量为32, 增长因子为2.0。
-
当使用Enqueue时,会判断队列的长度是否足够,若不足,则依据增长因子来增加容量,例如当为初始的2.0时,则队列容量增长2倍。
-
乏善可陈。
关于Queue<T>的使用方法,MSDN上也有相应的例子。
Stack<T>
与Queue<T>相对,当需要使用后进先出顺序(LIFO)的数据结构时,我们就需要用到Stack<T>了。
一些需要注意的地方:
-
后进先出的情景。
-
默认容量为10。
-
使用pop和push来操作。
-
乏善可陈。
同样,在MSDN你也可以看到大量Stack<T>的例子。
Dictionary<K,T>
字典这东西,小匹夫可是喜欢的不得了。看官们自己也可以想想字典是不是很招人喜欢,创建一个字典之后就可以往里面扔东西,增加、删除、访问那叫一个快字了 得。但是直到小匹夫日前看了一个大神的文章,才又想起了那句话“啥好事咋能让你都占了呢”。那么字典背后到底隐藏着什么迷雾,拨开重重迷雾之后,是否才是 真相?且听下回分。。。等等,应该是下面就让我们来分析一下字典吧。
提到字典就不得不说Hashtable哈希表以及Hashing(哈希,也有叫散列的),因为字典的实现方式就是哈希表的实现方式,只不过字典是类型安全的,也就是说当创建字典时,必须声明key和item的类型,这是第一条字典与哈希表的区别。关于哈希表的内容推荐看下这篇博客哈希表。关于哈希, 简单的说就是一种将任意长度的消息压缩到某一固定长度,比如某学校的学生学号范围从00000~99999,总共5位数字,若每个数字都对应一个索引的 话,那么就是100000个索引,但是如果我们使用后3位作为索引,那么索引的范围就变成了000~999了,当然会冲突的情况,这种情况就是哈希冲突(Hash Collisions)了。扯远了,关于具体的实现原理还是去看小匹夫推荐的那篇博客吧,当然那篇博客上面那个大大的转字也是蛮刺眼的。。。
回到Dictionary<K,T>,我们在对字典的操作中各种时间上的优势都享受到了,那么它的劣势到底在哪呢?对嘞,就是空间。以空 间换时间,通过更多的内存开销来满足我们对速度的追求。在创建字典时,我们可以传入一个容量值,但实际使用的容量并非该值。而是使用“不小于该值的最小质数来作为它使用的实际容量,最小是3。”(老赵),当有了实际容量之后,并非直接实现索引,而是通过创建额外的2个数组来实现间接的索引,即int[] buckets和Entry[] entries两个数组(即buckets中保存的其实是entries数组的下标),这里就是第二条字典与哈希表的区别,还记得哈希冲突吗?对,第二个区别就是处理哈希冲突的策略是不同的!字典会采用额外的数据结构来处理哈希冲突,这就是刚才提到的数组之一buckets桶了,buckets的长度就是字典的真实长度,因为buckets就是字典每个位置的映射,然后buckets中的每个元素都是一个链表,用来存储相同哈希的元素,然后再分配存储空间。
因此,我们面临的情况就是,即便我们新建了一个空的字典,那么伴随而来的是2个长度为3的数组。所以当处理的数据不多时,还是慎重使用字典为好,很多情况下使用数组也是可以接受的。
具体的使用方法:
下面是简单的例子,包括声明,填充键值对,移除键值对,遍历键值对 Dictionary<string, string> myDic = new Dictionary<string, string>(); myDic.Add("aaa", "111"); myDic.Add("bbb", "222"); myDic.Add("ccc", "333"); myDic.Add("ddd", "444"); //如果添加已经存在的键,add方法会抛出异常 try { myDic.Add("ddd","ddd"); } catch (ArgumentException ex) { Console.WriteLine("此键已经存在:" + ex.Message); } //解决add()异常的方法是用ContainsKey()方法来判断键是否存在 if (!myDic.ContainsKey("ddd")) { myDic.Add("ddd", "ddd"); } else { Console.WriteLine("此键已经存在:"); } //而使用索引器来负值时,如果建已经存在,就会修改已有的键的键值,而不会抛出异常 myDic ["ddd"]="ddd"; myDic["eee"] = "555"; //使用索引器来取值时,如果键不存在就会引发异常 try { Console.WriteLine("不存在的键""fff""的键值为:" + myDic["fff"]); } catch (KeyNotFoundException ex) { Console.WriteLine("没有找到键引发异常:" + ex.Message); } //解决上面的异常的方法是使用ContarnsKey() 来判断时候存在键,如果经常要取健值得化最好用 TryGetValue方法来获取集合中的对应键值 string value = ""; if (myDic.TryGetValue("fff", out value)) { Console.WriteLine("不存在的键""fff""的键值为:" + value ); } else { Console.WriteLine("没有找到对应键的键值"); } //下面用foreach 来遍历键值对 //泛型结构体 用来存储健值对 foreach (KeyValuePair<string, string> kvp in myDic) { Console.WriteLine("key={0},value={1}", kvp.Key, kvp.Value); } //获取值得集合 foreach (string s in myDic.Values) { Console.WriteLine("value={0}", s); } //获取值得另一种方式 Dictionary<string, string>.ValueCollection values = myDic.Values; foreach (string s in values) { Console.WriteLine("value={0}", s); } 常用的属性和方法如下: 常用属性 属性说明 Comparer 获取用于确定字典中的键是否相等的 IEqualityComparer。 Count 获取包含在 Dictionary中的键/值对的数目。 Item 获取或设置与指定的键相关联的值。 Keys 获取包含 Dictionary中的键的集合。 Values 获取包含 Dictionary中的值的集合。 常用的方法 方法说明 Add 将指定的键和值添加到字典中。 Clear 从 Dictionary中移除所有的键和值。 ContainsKey 确定 Dictionary是否包含指定的键。 ContainsValue 确定 Dictionary是否包含特定值。 Equals 已重载。 确定两个 Object 实例是否相等。 (从 Object 继承。) GetEnumerator 返回循环访问 Dictionary的枚举数。 GetHashCode 用作特定类型的哈希函数。GetHashCode 适合在哈希算法和数据结构(如哈希表)中使用。 (从 Object 继承。) GetObjectData 实现 System.Runtime.Serialization.ISerializable 接口,并返回序列化 Dictionary实例所需的数据。 GetType 获取当前实例的 Type。 (从 Object 继承。) OnDeserialization 实现 System.Runtime.Serialization.ISerializable接口,并在完成反序列化之后引发反序列化事件。 ReferenceEquals 确定指定的 Object实例是否是相同的实例。 (从 Object 继承。) Remove 从 Dictionary中移除所指定的键的值。 ToString 返回表示当前 Object的 String。 (从 Object 继承。) TryGetValue 获取与指定的键相关联的值。
2.几种常见数据结构的使用情景
C#常用集合的使用
大多数集合都在System.Collections,System.Collections.Generic两个命名空间。其中System.Collections.Generic专门用于泛型集合。
针对特定类型的集合类型位于System.Collections.Specialized;命名空间;
线程安全的集合类位于System.Collections.Concurrent;命名空间。
下面是集合和列表实现的接口如下:
一、列表
- [Serializable]
- [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
- [DebuggerDisplay("Count = {Count}")]
- public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
[Serializable]
[DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
[DebuggerDisplay("Count = {Count}")]
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
从这个可以看出,泛型集合List<T>实现了这么多接口,具体接口的信息可以通过工具查看。
- using System;
- using System.Collections.Generic;
- namespace ConsoleApplication1
- {
- public class Program
- {
- static void Main(string[] args)
- {
- List<String> list = new List<string>();
- list.Add("张三");
- list.Add("李四");
- list.Add("王五");
- list.Add("田六");
- list.Add("赵七");
- for (int i = 0; i < list.Count; i++)
- {
- Console.WriteLine("for循环:" + i.ToString() + "=" + list[i]);
- }
- list.RemoveAt(0);
- foreach (String item in list)
- {
- Console.WriteLine("foreach迭代:" + item);
- }
- list.AddRange(new String[] { "Hello1", "Hello2", "Hello3" });
- list.ForEach(Print);
- Console.Read();
- }
- private static void Print(String item)
- {
- Console.WriteLine("ForEach:" + item);
- }
- }
- }
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
List<String> list = new List<string>();
list.Add("张三");
list.Add("李四");
list.Add("王五");
list.Add("田六");
list.Add("赵七");
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine("for循环:" + i.ToString() + "=" + list[i]);
}
list.RemoveAt(0);
foreach (String item in list)
{
Console.WriteLine("foreach迭代:" + item);
}
list.AddRange(new String[] { "Hello1", "Hello2", "Hello3" });
list.ForEach(Print);
Console.Read();
}
private static void Print(String item)
{
Console.WriteLine("ForEach:" + item);
}
}
}
二、队列
队列先进先出,一头进一头出,用Queue<T>实现
- [Serializable]
- [DebuggerTypeProxy(typeof(System_QueueDebugView<>))]
- [ComVisible(false)]
- [DebuggerDisplay("Count = {Count}")]
- public class Queue<T> : IEnumerable<T>, ICollection, IEnumerable
[Serializable]
[DebuggerTypeProxy(typeof(System_QueueDebugView<>))]
[ComVisible(false)]
[DebuggerDisplay("Count = {Count}")]
public class Queue<T> : IEnumerable<T>, ICollection, IEnumerable
可以看出队列实现了集合的接口,迭代的接口
- using System;
- using System.Collections.Generic;
- namespace ConsoleApplication1
- {
- public class Program
- {
- static void Main(string[] args)
- {
- Queue<String> queue = new Queue<string>();
- //进队
- queue.Enqueue("张三");
- queue.Enqueue("李四");
- queue.Enqueue("王五");
- queue.Enqueue("田六");
- queue.Enqueue("赵七");
- foreach (String item in queue)
- {
- Console.WriteLine("foreach迭代:" + item);
- }
- //出队
- while (queue.Count > 0)
- {
- Console.WriteLine("出队:" + queue.Dequeue());
- }
- Console.Read();
- }
- }
- }
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
Queue<String> queue = new Queue<string>();
//进队
queue.Enqueue("张三");
queue.Enqueue("李四");
queue.Enqueue("王五");
queue.Enqueue("田六");
queue.Enqueue("赵七");
foreach (String item in queue)
{
Console.WriteLine("foreach迭代:" + item);
}
//出队
while (queue.Count > 0)
{
Console.WriteLine("出队:" + queue.Dequeue());
}
Console.Read();
}
}
}
三、栈
栈:从同一边先进后出,用Stack<T>实现
- [DebuggerDisplay("Count = {Count}")]
- [DebuggerTypeProxy(typeof(System_StackDebugView<>))]
- [ComVisible(false)]
- public class Stack<T> : IEnumerable<T>, ICollection, IEnumerable
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(System_StackDebugView<>))]
[ComVisible(false)]
public class Stack<T> : IEnumerable<T>, ICollection, IEnumerable
栈也是实现了集合接口与迭代接口的
- using System;
- using System.Collections.Generic;
- namespace ConsoleApplication1
- {
- public class Program
- {
- static void Main(string[] args)
- {
- Stack<String> stack = new Stack<string>();
- //进栈
- stack.Push("张三");
- stack.Push("李四");
- stack.Push("王五");
- stack.Push("田六");
- stack.Push("赵七");
- foreach (String item in stack)
- {
- Console.WriteLine("foreach迭代:" + item);
- }
- //出栈
- while (stack.Count > 0)
- {
- Console.WriteLine("出栈:" + stack.Pop());
- }
- Console.Read();
- }
- }
- }
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
Stack<String> stack = new Stack<string>();
//进栈
stack.Push("张三");
stack.Push("李四");
stack.Push("王五");
stack.Push("田六");
stack.Push("赵七");
foreach (String item in stack)
{
Console.WriteLine("foreach迭代:" + item);
}
//出栈
while (stack.Count > 0)
{
Console.WriteLine("出栈:" + stack.Pop());
}
Console.Read();
}
}
}
四、链表
LinkedList是一个双向链表,链表有个有点,就是在链表中间插入、删除元素很快,但是查找中间与末尾的元素很慢,需要一个节点一个节点的去找。
- [Serializable]
- [DebuggerTypeProxy(typeof(System_CollectionDebugView<>))]
- [DebuggerDisplay("Count = {Count}")]
- [ComVisible(false)]
- public class LinkedList<T> : ICollection<T>, IEnumerable<T>, ICollection, IEnumerable, ISerializable, IDeserializationCallback
[Serializable]
[DebuggerTypeProxy(typeof(System_CollectionDebugView<>))]
[DebuggerDisplay("Count = {Count}")]
[ComVisible(false)]
public class LinkedList<T> : ICollection<T>, IEnumerable<T>, ICollection, IEnumerable, ISerializable, IDeserializationCallback
由此可见链表也是有集合的特性的,可以迭代,同时还有链表的特性
- using System;
- using System.Collections.Generic;
- namespace ConsoleApplication1
- {
- public class Program
- {
- static void Main(string[] args)
- {
- LinkedList<String> lList = new LinkedList<string>();
- LinkedListNode<String> node = new LinkedListNode<string>("root");
- lList.AddFirst(node);
- node = lList.AddAfter(node, "张三");
- node = lList.AddAfter(node, "李四");
- node = lList.AddAfter(node, "王五");
- node = lList.AddAfter(node, "田六");
- node = lList.AddAfter(node, "赵七");
- foreach (String item in lList)
- {
- Console.WriteLine("foreach迭代:" + item);
- }
- node = lList.First;
- Console.WriteLine("第一个元素:" + node.Value);
- node = lList.Last;
- Console.WriteLine("最后一个元素:" + node.Value);
- Console.Read();
- }
- }
- }
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
LinkedList<String> lList = new LinkedList<string>();
LinkedListNode<String> node = new LinkedListNode<string>("root");
lList.AddFirst(node);
node = lList.AddAfter(node, "张三");
node = lList.AddAfter(node, "李四");
node = lList.AddAfter(node, "王五");
node = lList.AddAfter(node, "田六");
node = lList.AddAfter(node, "赵七");
foreach (String item in lList)
{
Console.WriteLine("foreach迭代:" + item);
}
node = lList.First;
Console.WriteLine("第一个元素:" + node.Value);
node = lList.Last;
Console.WriteLine("最后一个元素:" + node.Value);
Console.Read();
}
}
}
五、有序列表
SortedList采用键-值对存储,键不能重复,并且会根据key进行排序
- [Serializable]
- [DebuggerTypeProxy(typeof(System_DictionaryDebugView<,>))]
- [DebuggerDisplay("Count = {Count}")]
- [ComVisible(false)]
- public class SortedList<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable
[Serializable]
[DebuggerTypeProxy(typeof(System_DictionaryDebugView<,>))]
[DebuggerDisplay("Count = {Count}")]
[ComVisible(false)]
public class SortedList<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable
可以看出SortedList不仅具有字典的特性,还有集合,迭代的功能
- using System;
- using System.Collections.Generic;
- namespace ConsoleApplication1
- {
- public class Program
- {
- static void Main(string[] args)
- {
- //Key必须唯一,如果不唯一可以考虑Lookup<TKey,TElement>
- SortedList<int, String> sList = new SortedList<int, string>();
- sList.Add(100, "张三");
- sList.Add(21, "李四");
- sList.Add(13, "王五");
- sList.Add(44, "田六");
- sList.Add(35, "赵七");
- foreach (KeyValuePair<int, String> item in sList)
- {
- Console.WriteLine("key=" + item.Key.ToString() + ";value=" + item.Value);
- }
- Console.Read();
- }
- }
- }
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
//Key必须唯一,如果不唯一可以考虑Lookup<TKey,TElement>
SortedList<int, String> sList = new SortedList<int, string>();
sList.Add(100, "张三");
sList.Add(21, "李四");
sList.Add(13, "王五");
sList.Add(44, "田六");
sList.Add(35, "赵七");
foreach (KeyValuePair<int, String> item in sList)
{
Console.WriteLine("key=" + item.Key.ToString() + ";value=" + item.Value);
}
Console.Read();
}
}
}
六、字典
字典是很复杂的数据结构,允许通过key来查找值,字典可以自由添加、删除元素,没有集合由于移动元素导致的开销。
- [Serializable]
- [DebuggerTypeProxy(typeof(Mscorlib_DictionaryDebugView<,>))]
- [DebuggerDisplay("Count = {Count}")]
- [ComVisible(false)]
- public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback
[Serializable]
[DebuggerTypeProxy(typeof(Mscorlib_DictionaryDebugView<,>))]
[DebuggerDisplay("Count = {Count}")]
[ComVisible(false)]
public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback
可以看出字典也具有集合的特性,可以迭代
- using System;
- using System.Collections.Generic;
- namespace ConsoleApplication1
- {
- public class Program
- {
- static void Main(string[] args)
- {
- //Key必须唯一
- Dictionary<int, String> dict = new Dictionary<int, string>();
- dict.Add(11, "张三");
- dict.Add(1, "李四");
- dict.Add(2, "王五");
- dict.Add(16, "田六");
- dict.Add(12, "赵七");
- foreach (KeyValuePair<int, String> item in dict)
- {
- Console.WriteLine("key=" + item.Key.ToString() + ";value=" + item.Value);
- }
- Console.Read();
- }
- }
- }
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
//Key必须唯一
Dictionary<int, String> dict = new Dictionary<int, string>();
dict.Add(11, "张三");
dict.Add(1, "李四");
dict.Add(2, "王五");
dict.Add(16, "田六");
dict.Add(12, "赵七");
foreach (KeyValuePair<int, String> item in dict)
{
Console.WriteLine("key=" + item.Key.ToString() + ";value=" + item.Value);
}
Console.Read();
}
}
}
说到字典,顺便谈一下有序字典,与有序列表对应;SortedDictionary,SortedList,SortedSet
会根据Key进行排序
- using System;
- using System.Collections.Generic;
- namespace ConsoleApplication1
- {
- public class Program
- {
- static void Main(string[] args)
- {
- //Key必须唯一
- SortedDictionary<int, String> dict = new SortedDictionary<int, string>();
- dict.Add(11, "张三");
- dict.Add(1, "李四");
- dict.Add(2, "王五");
- dict.Add(16, "田六");
- dict.Add(12, "赵七");
- foreach (KeyValuePair<int, String> item in dict)
- {
- Console.WriteLine("key=" + item.Key.ToString() + ";value=" + item.Value);
- }
- Console.Read();
- }
- }
- }
Hashtable(哈希表)的用法
Student.Add("Kevin", 100);
Student.Add("Kevsef", 50);
Student.Add("Keafdan", 19);
Student.Add("afcvin", 90);
foreach (DictionaryEntry obj in Student)
{
Debug.LogError("Test: " + obj.Key);
Debug.LogError("Test: " + obj.Value);
Debug.LogError("Test: " + Student.ContainsKey("Kevin"));
Debug.LogError("Test: " + Student.ContainsKey("100"));
}
Queue<string> myQueue = new Queue<string>(myStack);//把队列转化为栈
List<string> myList = new List<string>(myQueue); //把栈转化为列表
Stack<string> anotherList = new Stack<string>(myList);//把列表转化为队列
void Test()
{
myStack.Push("First");
myStack.Push("Second");
myStack.Push("Third");
}