数据结构
- 数据结构是计算机存储、组织、管理数据的方式
- 数据结构是指相互之间存在一种或多种特定关系的数据元素的集合
一、数组的定义
Array
也就是数组。具体表示方法是:数据类型[维数] 数组名=new 数据类型[]
数组有很多的优点,比如说数组在内存中是连续存储的,所以它的索引速度是非常的快,而且赋值与修改元素也很简单,
比如:
//声明一个一维数组
intArray1 = new int[3];
//初始化一个一维数组
intArray1 = new int[3]{1,2,3};
intArray1 = new int[]{1,2,3};
//赋值
s[0]="a";
s[1]="b";
s[2]="c";
//修改
s[1]="b1";
//声明一个二维数组
int[,] cells=int[3,3];
int[,] cells={{1,0,2},{1,2,0},{1,2,1}}; //初始化一个二维整数数组
数组的不足之处在于,使用数组管理数据时,需要预先知道数组长度。而实际开发中,很多数据往往是无法事先知道元素个数的。
数组是一种非常有用的数据结构,但数组也具有严重的局限性。
①数组元素的数据类型必须是相同的
②在创建数组时必须知道元素个数
③对应用程序来说,需要通过循环索引来访问元素。
针对于数组的这些缺点,C#中最先提供了ArrayList对象来克服这些缺点。
适应于知道数组长度并且在使用过程中不需要插入元素的情况。
ArrayList
动态数组,用法似乎跟c++的vector有点像。使用ArrayList必须引用Collections类。
用于数据存储和检索的专用类。它的大小是按照其中存储的数据来动态扩充与收缩的。所以,我们在声明ArrayList对象时并不需要指定它的长度。
ArrayList继承了IList接口,所以它可以很方便的进行数据的添加,插入和移除。比如:
ArrayList list = new ArrayList(); //新增数据
list.Add("abc"); list.Add(123); //修改数据
list[2] = 345; //移除数据
list.RemoveAt(0); //插入数据
list.Insert(0, "hello world");
添加
- Add(a) 添加元素a到末尾;
- Insert(b,a) 在位置b插入元素a;
- InsertRange(b,a) 在位置b插入集合a;
删除
- Remove(a) 移除元素a;
- RemoveAt(a) 移除位置a的元素;
- RemoveRange(a,b) 移除位置a到位置b的元素;
- Clear() 清空;
排序 Sort();
反转 Reverse();
查找
- IndexOf(a) 返回元素a的位置,没有则返回-1;
- Contains(a) 检测是否含有元素a,返回true/false;
从上面示例看,ArrayList好像是解决了数组中所有的缺点,在list中,我们不仅插入了字符串"abc",而且又插入了数字123。这样在ArrayList中插入不同类型的数据是允许的。因为ArrayList会把所有插入其中的数据都当作为object类型来处理。这样,在我们使用ArrayList中的数据来处理问题的时候,很可能会报类型不匹配的错误,也就是说ArrayList不是类型安全的。
既使我们保证在插入数据的时候都很小心,都有插入了同一类型的数据,但在使用的时候,我们也需要将它们转化为对应的原类型来处理。这就存在了装箱与拆箱的操作,会带来很大的性能损耗。
穿插一下装箱与拆箱的概念: 简单的来讲: 装箱:就是将值类型的数据打包到引用类型的实例中 比如将int类型的值123赋给object对象o int i=123; object o=(object)i; 拆箱:就是从引用数据中提取值类型 比如将object对象o的值赋给int类型的变量i object o=123; int i=(int)o; 装箱与拆箱的过程是很损耗性能的。
ArrayList与Array的区别
- Array大小是固定的,ArrayList大小可按需自动扩容。
- Array的特点是类型统一且长度固定,ArrayList是可变长数组。
- Array一次只能获取或设置一个元素的值,ArrayList允许添加、插入、移除某个范围的元素。
- Array下限可自定义,ArrayList下限始终未0。
- Array具有多维,ArrayList始终只有一维。
- Array位于System命名空间中,ArrayList位于System.Collections命名空间中。
- ArrayList是非泛型列表,可见任意Object类型作为元素。
二、集合的定义
数组并不是使用最为方便的数据结构。C#提供了集合,通过它来管理数据将更为方便。
C#提供了一系列特殊功能的类,这些类可存储任意类型的对象,且长度是可变的,统称为集合。C#编程语言中,集合结构分为泛型集合、非泛型集合。
常用集合
- 数组 Array
- 列表 List:List<T>是与数组相当的集合类
- 动态数组 ArrayList、List<T>
- 键值对集合
- 哈希集合/哈希表 Hashtable
- 字典 Dictionary<K,V>
- 堆栈 Stack、Stack<T>,堆栈的特点是后进先出(LIFO, Last In First Out)。
- 队列 Queue、Queue<T>,队列的特点是先进先出(FIFO, First In First Out)。
- 可排序键值对:特点是插入、检索没有哈希表集合高效
- 有序列键值对列表 SortedList、SortedList<K,V> 特点是占用内存更少,可通过索引访问。
- 有序字典 SortedDictionary 特点是占用内存更多,没有索引,但插入和删除元素的速度比SortedList快。
- Set集合:特点是无序、不重复。
- HashSet<T>集合可将HashSet类视为不包含值得Dictionary集合,与List<T>类似。
- SortedSet<T> 在.NET4.0支持,有序无重复的集合。
- 双向链表集合:LinkedList<T>特点是增删速度快
理解泛型与非泛型的概念
1、BCL中集合类型分为泛型集合与非泛型集合。
2、非泛型集合的类和接口位于System.Collections命名空间。
3、泛型集合的类和接口位于System.Collections.Generic命名空间。
同传统的集合相比,泛型集合是一种强类型的集合,它解决了类型安全问题,同时避免了集合中每次的装箱与拆箱的操作,提升了性能。
1. List,这是我们应用最多的泛型种类,它对应ArrayList集合。
2. Dictionary,这也是我们平时运用比较多的泛型种类,对应Hashtable集合。
3. Collection对应于CollectionBase
4. ReadOnlyCollection 对应于ReadOnlyCollectionBase,这是一个只读的集合。
5. Queue,Stack和SortedList,它们分别对应于与它们同名的非泛型类。
C#2.0后出现了List。
泛型集合:
①List
List names = new List();
names.Add("乔峰");
names.Add("欧阳峰");
names.Add("马蜂");
foreach (string name in names)
{
Console.WriteLine(name);
}
//向List中插入元素
names.Insert(2, "张三峰");
//移除指定元素
names.Remove("马蜂");
②Dictionary
System.Collections.DictionaryEntry dic=new System.Collections.DictionaryEntry("key1","value1");
Dictionary fruit = new Dictionary(); //加入重复键会引发异常
fruit.Add(1, "苹果");
fruit.Add(2, "桔子");
fruit.Add(3, "香蕉");
fruit.Add(4, "菠萝"); //因为引入了泛型,所以键取出后不需要进行Object到int的转换,值的集合也一样
foreach (int i in fruit.Keys) {
Console.WriteLine("键是:{0} 值是:{1}",i,fruit);
}
//删除指定键值
fruit.Remove(1);
//判断是否包含指定键
if (fruit.ContainsKey(1)) {
Console.WriteLine("包含此键");
}
//清除集合中所有对象
fruit.Clear();
SortedList类 与哈希表类似,区别在于SortedList中的Key数组排好序的
Stack类 栈,后进先出。push方法入栈,pop方法出栈。
Queue类 队列,先进先出。enqueue方法入队列,dequeue方法出队列。
非泛型集合:
①Hashtable
Hashtable abc = new Hashtable();
abc.Add("1", "34"); (key,value)
if (abc.Contains("1"))
{
Response.Write(abc["1"]);
}
Hashtable与Dictionary异同点
- 在单线程程序中推荐使用Dictionary,由于具有泛型优势且读取速度较快,其容量利用会更加充分。
- 在多线程程序中推荐使用Hashtable,默认的Hashtable允许单线程写入多线程读取,对Hashtable进一步调用Synchronized()方法可获得完全线程安全的类型。而Dictionary是非线程安全的,必须人为使用lock语句进行保护,其效率大减。
- 经过测试发现,但key是整型时Dictionary的操作效率要高于Hashtable,而当key是字符串时则Dictionary的效率并没有Hashtable的快。
- Dictionary有按插入顺序排列数据的特性,但是当调用Remove()方法删除节点后原来的顺序会被打乱掉。因此在需要体现顺序的场景中使用Dictionary能获得一定的便利。
- 遍历Dictionary时会采用插入时的顺序,而遍历Hashtable则是打乱掉的。
哈希表的遍历
哈希表的遍历使用foreach,由于哈希表中的元素是键值对,因此需要使用DictionaryEntry类型来进行遍历。DictionaryEntry类型表示一个键值对的集合。
Hashtable ht = new Hashtable();
ht.Add("UserID", 1);
ht.Add("UserNO", "BH0001");
ht.Add("UserName", "Tim");
ht.Add("UserGender", 1); foreach(DictionaryEntry dic in ht){
Console.WriteLine($"{dic.Key} = {dic.Value}");}
②Queue
System.Collections.Queue queue=new System.Collections.Queue();
queue.Enqueue(1);
queue.Enqueue(2);
System.Console.WriteLine(queue.Peek());
while(queue.Count>0) {
System.Console.WriteLine(queue.Dequeue());
}
③SortedList
System.Collections.SortedList list=new System.Collections.SortedList();
list.Add("key2",2);
list.Add("key1",1);
for(int i=0;i0) {
System.Console.WriteLine(stack.Pop());
}