在日常开发过程中,我们不能避免的要对批量数据处理,这时候就要用到集合。集合总体上分为线性集合和非线性集合。线性集合是指元素具有唯一的前驱和后驱的数据结构类型;非线性集合是指有多个前驱和后驱的数据结构类型,如树和图。我们这里主要讲常用的线性集合,常用的线性集合有数组、ArrayList、List、Hashtable(哈希表)、Dictionary(字典)、Stack(堆栈集合)、Queue(队列集合)等。
一、数组
数组是一个存储相同类型元素的固定大小的顺序集合。数组属于引用类型,它继承System.Array类,System.Array是所有数组的基类。
1、一维数组
一维数组声明方式:
//声明一个长度为5的int类型一维数组 int[] numbers = new int[5];
一维数组的初始化方式有以下三种写法:
//一维数组的初始化方式 int[] numbers1 = new int[5] {1,2,3,4,5};//指定大小和元素,使用条件是数组大小必须与元素个数相匹配。 int[] numbers2 = new int[] {1,2,3,4,5};//不指定数组大小,因为编译器会自动统计元素的个数。 int[] numbers3 = { 1, 2, 3, 4, 5 };//这是更简化的形式,直接使用花括号声明和初始化数组。
访问数组元素:
在声明和初始化数组后,就可以使用索引器访问其中的元素了,数组只支持有整型参数的索引器。索引器总是以0开头,表示第一个元素。注意:如果使用错误的索引器值(其中不存在对应的元素),就会抛出IndexOutOfRangeExcepation类型的异常。
int[] numbers1 = new int[5] {1,2,3,4,5}; int value1 = numbers1[0];//获取数组的第一个元素。
如果要循环遍历数组中的元素,可以使用for循环,也可以使用foreach来遍历数组中的元素,两者的区别在于:foreach循环对数组内容进行只读访问,所以不能改变任何元素的值,但是for循环可以给数组元素赋值。
2、多维数组
多维数组包含二维数组、三维数组等等。我们这里以二维数组为例。
二维数组声明方式:
//二维数组的声明方式, string[,] names = new string[5, 4];
二维数组初始化方式:
//二维数组的初始化方式 string[,] names1 = { { "a", "b" }, { "c", "d" } };
3、数组的数组
数组的数组声明方式:
//数组的数组声明方式 byte[][] scores = new byte[5][];
数组的数组初始化方式:
//数组的数组的初始化方式 int[][] scores1 = { new int[]{1,2},new int[]{1,2,3,4}};
多维数组与数组的数组的差别:多维数组每行长度一致,是固定的;但是数组的数组每行长度是可以不固定。数组在内存中是连续存储的,所以它的索引速度非常快,但也正因为数组在内存中是连续存储的,造成数组必须在声明时指定大小,那么如果声明时数组长度太长,则导致内存浪费;长度太短又会造成数据溢出错误。另外,因为空间连续,插入和删除元素效率比较低,而且麻烦。
二、集合ArrayList和List
1、ArrayList
ArrayList是System.Collections命名空间中定义的非泛型类型的集合。它用于创建动态数组意味着数组的大小根据程序的要求自动增加或减少,不需要指定ArrayList的大小。因为ArrayList是非泛型类型的集合,所以ArrayList既能存储相同类型的元素,也能存储不同类型的元素,故ArrayList不能保证类型安全。
注意:ArrayList的使用需要引入System.Collections命名空间。
ArrayList常用方法:
ArrayList arrayList = new ArrayList(); ArrayList tempArrayList = new ArrayList(); arrayList.Add("abc");//在集合结尾处添加单个元素 arrayList.AddRange(tempArrayList);//在集合结尾处添加指定集合 arrayList.Remove("abc");//移除指定元素(第一个匹配项) arrayList.RemoveAt(0);//移除指定索引处元素 arrayList.Contains("cd");//判断指定元素是否存在集合中。 arrayList.Insert(1,"kk");//将元素插入指定索引位置
通过ArrayList的Add()方法,我们不难看出向ArrayList集合中添加元素时是以Object类型添加的,也就是说添加进ArrayList集合的所有元素将转换成Object类型。如果我们向ArrayList集合添加的元素是值类型的时候,这个时候将涉及到拆箱和装箱的操作,在我们之前的内容中提到拆箱和装箱是很消耗内存和cpu资源的。另外,即使我们向ArrayList集合添加的元素是引用类型,那么我们在使用时也需要进行类型转换,这个过程也是需要耗费时间和资源的。
2、List
List是一种泛型集合,它是由ArrayList发展而来的集合,在保留ArrayList优点的同时也解决了ArrayList对于值类型需要拆箱和装箱的缺点,并保证类型安全。因此List在声明时就需要指定类型。
注意:List的使用需要引入System.Collections.Generic命名空间。
List常用方法:
List<string> address = new List<string>();//声明一个List List<string> tempAddress = new List<string>() {"北京","沈阳"}; address.Add("武汉");//在集合结尾处添加单个元素 address.AddRange(tempAddress);//在集合结尾处添加指定集合 address.Remove("沈阳");//移除集合中第一个匹配元素 address.RemoveAt(0);//移除指定索引处元素 address.Insert(1, "湖南");//将元素插入集合指定索引位置 address.Clear();//移除集合中所有元素 address.Contains("湖南");//判断某个元素是否存在集合中 int number = address.Count;//获取集合中元素个数 //List还有很多其它方法,这里就不详细讲解。
通过ArrayList和List的对比,我们在日常开发的过程中,尽量避免使用ArrayList,如果集合的数目固定并且不涉及类型转换,使用数组效率高,否则使用List。
三、Hashtable和Dictionary
C#中的Hashtable和Dictionary都是以键值对形式存储的集合,哈希表未指定类型,会引发类型安全问题,同时使用时也会进行拆箱和装箱操作,比较耗费资源;字典指定类型,故保证类型安全,但字典多线程时不是线程安全的类型,必须人为使用lock语句进行保护,这样效率会大减。哈希表默认是允许单线程写入,多线程读取,故是线程安全类型。
哈希表常用方法:
//哈希表未声明类型,会引发类型安全问题 Hashtable hashtable = new Hashtable();//声明一个哈希表 hashtable.Add(0,"北京");//添加带有指定键和值的元素 hashtable.Clear();//移除所有元素 hashtable.ContainsKey(0);//判断哈希表中是否存在指定键 hashtable.ContainsValue("北京");//判断哈希表中是否存在指定值 hashtable.Remove(0);//移除指定键的键值对 int hashtableCount = hashtable.Count;//获取哈希表中键值对个数
字典常用方法:
//字典指定数据类型 Dictionary<string, string> dictionary = new Dictionary<string, string>();//声明一个字典 dictionary.Add("张三","15823504311");//添加带有指定键和值的元素 dictionary.Clear();//移除所有元素 dictionary.ContainsKey("张三");//判断字典中是否存在指定键 dictionary.ContainsValue("15823504311");//判断字典中是否存在指定值 dictionary.Remove("张三");//移除指定键的键值对 int dictionaryCount = dictionary.Count;//获取字典中键值对个数 dictionary.Keys;//获取字典中键的集合 dictionary.Values;//获取字典中值的集合
四、其它常用集合类型
1、队列Queue<T>
队列Queue<T>遵循的是先入先出的模式,它在集合末尾添加元素,在集合的起始位置删除元素。根据队列的特点,可以用它来处理并发命令等场景:先让所有客户端的命令入队,然后,由专门的工作线程来执行队列的命令。在分布式中的消息队列就是一个典型的队列
应用实例。
2、栈Stack<T>
栈Stack<T> 遵循的是后入先出的模式,它在集合末尾添加元素,同时也在集合末尾删除元素。
3、双向链表LinkedList<T>
双向链表LinkedList<T>是一个类型为LinkedListNode的元素对象的集合。当我们觉得在集合中插入和删除数据很慢时,就可以考虑使用链表。双向链表常用方法为AddAfter、AddBefore、AddFirst、AddLast等,双向链表中的每个节点都向前指向Previous节点,向后指向Next节点。