【C#进阶】C# 集合类

| 序号 | 系列文章 |
|---|---|
| 16 | 【C#进阶】C# 索引器 |
| 17 | 【C#进阶】C# 委托 |
| 18 | 【C#进阶】C# 事件 |
文章目录
- 前言
- 1、集合类是什么
- 2、动态数组(ArrayList)
- 3、压缩数组(BitArray)
- 4、哈希表(Hashtable)
- 5、队列(Queue)
- 6、排序列表(SortedList)
- 7、堆栈(Stack)
- 结语
前言
🌄 hello大家好啊,我是哈桑,本章为大家介绍 C# 中的集合类。
1、集合类是什么
集合类是专门用于数据存储和检索的类。在这些类中实现了对列表、队列、哈希表等数据结构的封装以及对操作数据的支持。当在项目中需要对不同场景的数据进存储和操作时,就会使用到集合类。选择合适集合对象,能够在一定程度提升代码的性能和降低内存占用。
在 .NET 框架中,提供了一个叫 System.Collections 的命名空间,其中包含定义各种对象集合(包括但不限于列表、队列、位数组、哈希表和字典)的类和接口。在 C# 中,当我们需要引用这其中的对象集合时,就需要引入 System.Collections 的命名空间。
System.Collections 命名空间部分源码:

接下来列举在 System.Collections 命名空间中常用的集合类以及用法:
2、动态数组(ArrayList)
区别于常见的静态数组,动态数组指的是在声明时不必确定大小的数组,在实际的项目中,动态数组基本上可以取代一个静态数组的功能。操作动态数组的大小时会根据动态增加的元素来实现 ILIst 接口,也就是可以在任何时候改变数组的大小。动态数组有着灵活、方便和高效利用存储空间的优点。
示例代码:
using System;
using System.Collections;public class SamplesArrayList
{public static void Main(){// 创建并初始化一个新的数组列表。 ArrayList myAL = new ArrayList();myAL.Add("Hello");myAL.Add("World");myAL.Add("!");PrintAttribute(myAL); }// 输出动态数组的值public static void PrintAttribute(ArrayList myList){// 显示数组列表的属性和值。 Console.WriteLine("动态数组myAL");Console.WriteLine("数组大小:{0}", myList.Count);Console.WriteLine("数组容量:{0}", myList.Capacity);Console.Write("数组元素值:"); foreach (Object obj in myList){Console.Write($"{obj}");}Console.ReadLine(); }
}
运行结果:

以上例中,演示了如何创建和初始化一个动态数组以及如何使用数组属性和显示其元素值。
ArrayList 类中一些常用的属性:
| 属性名 | 描述 |
|---|---|
| Capacity | 获取或设置 ArrayList 可包含的元素数。 |
| Count | 获取 ArrayList 中实际包含的元素数。 |
| IsFixedSize | 获取一个值,该值指示 ArrayList 是否具有固定大小。 |
| IsReadOnly | 获取一个值,该值指示 ArrayList 是否为只读。 |
| Item[Int32] | 获取或设置指定索引处的元素。 |
ArrayList 类中一些常用的方法:
| 方法名 | 描述 |
|---|---|
| Add(Object) | 将对象添加到 ArrayList 的结尾处。 |
| AddRange(ICollection) | 将 ICollection 的元素添加到 ArrayList 的末尾。 |
| BinarySearch(Object) | 使用默认的比较器在整个已排序的 ArrayList 中搜索元素,并返回该元素从零开始的索引。 |
| Clear() | 从 ArrayList 中移除所有元素。 |
| Clone() | 创建 ArrayList 的浅表副本。 |
| Contains(Object) | 确定某元素是否在 ArrayList 中。 |
| CopyTo(Array) | 从目标数组的开头开始,将整个 ArrayList 复制到兼容的一维 Array。 |
| Equals(Object) | 确定指定对象是否等于当前对象。(继承自 Object) |
| FixedSize(ArrayList) | 返回具有固定大小的 ArrayList 包装。 |
| GetRange(Int32, Int32) | 返回一个 ArrayList,它表示源 ArrayList 中的元素子集。 |
| GetType() | 获取当前实例的 Type。(继承自 Object) |
| IndexOf(Object) | 搜索指定的 Object,并返回整个 ArrayList 中第一个匹配项的从零开始的索引。 |
| Insert(Int32, Object) | 将元素插入 ArrayList 的指定索引处。 |
| InsertRange(Int32, ICollection) | 将集合中的元素插入 ArrayList 的指定索引处。 |
| LastIndexOf(Object) | 在整个 ArrayList 中搜索指定的 Object,并返回最后一个匹配项的从零开始的索引。 |
| ReadOnly(ArrayList) | 返回只读的 ArrayList 包装。 |
| Remove(Object) | 从 ArrayList 中移除特定对象的第一个匹配项。 |
| RemoveAt(Int32) | 移除 ArrayList 的指定索引处的元素。 |
| RemoveRange(Int32, Int32) | 从 ArrayList 中移除一系列元素。 |
| Reverse() | 将整个 ArrayList 中元素的顺序反转。 |
| Reverse(Int32, Int32) | 将指定范围中元素的顺序反转。 |
| Sort() | 对整个 ArrayList 中的元素进行排序。 |
| ToArray() | 将 ArrayList 的元素复制到新 Object 数组中。 |
| ToString() | 返回表示当前对象的字符串。(继承自 Object) |
3、压缩数组(BitArray)
压缩数组是一个紧凑型的位值数组,这些值以布尔值得形式表示,其中 true 表示此位为开 (1),false 表示此位为关 (0)。 当一个数组中大部分元素为0或者1时,又或者为同一个值时,就可以使用压缩数组来表示。
示例代码:
using System;
using System.Collections;public class SamplesBitArray
{public static void Main(){// 创建并初始化压缩数组 bitarray。 BitArray myBA1 = new BitArray(15);BitArray myBA2 = new BitArray(5, false);byte[] myBytes = new byte[6] {1, 2, 3, 4, 5, 6};// 传入参数为一个数组时 BitArray myBA3 = new BitArray(myBytes);bool[] myBools = new bool[5] { true, false, true, true, false };BitArray myBA4 = new BitArray(myBools);//显示BitArrays的属性和值。 Console.WriteLine("myBA1");Console.WriteLine("元素个数:{0}", myBA1.Count);Console.WriteLine("数组容量:{0}", myBA1.Length);Console.WriteLine("数组元素值:");PrintValues(myBA1, 8);Console.WriteLine("myBA2");Console.WriteLine("元素个数:{0}", myBA2.Count);Console.WriteLine("数组容量:{0}", myBA2.Length);Console.WriteLine("数组元素值:");PrintValues(myBA2, 8);Console.WriteLine("myBA3");Console.WriteLine("元素个数:{0}", myBA3.Count);Console.WriteLine("数组容量:{0}", myBA3.Length);Console.WriteLine("数组元素值:");PrintValues(myBA3, 8);Console.WriteLine("myBA4");Console.WriteLine("元素个数:{0}", myBA4.Count);Console.WriteLine("数组容量:{0}", myBA4.Length);Console.WriteLine("数组元素值:");PrintValues(myBA4, 8);}public static void PrintValues(IEnumerable myList, int myWidth){// myWidth表示每行输出得元素个数 int i = myWidth;foreach (Object obj in myList){if (i <= 0){i = myWidth;Console.WriteLine();}i--;Console.Write("{0,8}", obj);} Console.WriteLine();}
}
运行结果:

在上例中,我们可以实例化 BitArray 类的对象,传入不同类型的参数生成不同压缩数组。若传入的是一个整数,则表示该数组的元素个数和容量,默认位值为 false,可以指定第二参数修改默认值。若传入的是一个整数数组,则表示输出数组元素对应的位表达,例如1的位表达为:True False False False False False False False,其中 True 表示1,False 表示0。当输出的参数为一个布尔数组时,则直接输出布尔数组表示对应字节表达。
BitArray 类中一些常用的属性:
| 属性名 | 描述 |
|---|---|
| Count | 获取 BitArray 中包含的元素数。 |
| IsReadOnly | 获取一个值,该值指示 BitArray 是否为只读。 |
| Item[Int32] | 获取或设置 BitArray 中特定位置的位的值。 |
| Length | 获取或设置 BitArray 中的元素数。 |
BitArray 类中一些常用的方法:
| 方法名 | 描述 |
|---|---|
| And(BitArray) | 在当前 BitArray 对象中的元素和指定数组中的相应元素之间执行按位“与”运算。 将修改当前 BitArray 对象,以存储按位“与”运算的结果。 |
| Clone() | 创建 BitArray 的浅表副本。 |
| CopyTo(Array, Int32) | 从目标数组的指定索引处开始将整个 BitArray 复制到兼容的一维 Array。 |
| Equals(Object) | 确定指定对象是否等于当前对象。(继承自 Object) |
| Get(Int32) | 获取 BitArray 中特定位置处的位的值。 |
| Set(Int32, Boolean) | 将 BitArray 中特定位置处的位设置为指定值。 |
| SetAll(Boolean) | 将 BitArray 中的所有位设置为指定值。 |
| ToString() | 返回表示当前对象的字符串。(继承自 Object) |
4、哈希表(Hashtable)
哈希表是一个根据键的哈希码1进行组织的键/值对的集合。在哈希表中,可以使用键来访问对应的元素值。有时候正确的使用哈希表,可以显著的提高代码的性能,比方说需要在项目中实现高效的数据存储和查找的时候。
示例代码:
using System;
using System.Collections;public class SamplesHashtable
{public static void Main(){// 创建一个新的哈希表对象 Hashtable openWith = new Hashtable();// 添加一些元素到哈希表中。没有重复的键,但是有些值是重复的。openWith.Add("txt", "notepad.exe");openWith.Add("bmp", "paint.exe");openWith.Add("dib", "paint.exe");openWith.Add("rtf", "wordpad.exe");// 如果新键为,Add方法抛出异常// 已经在哈希表中。try{openWith.Add("txt", "winword.exe");}catch{Console.WriteLine("带有Key的元素 = \\"txt\\" .");}// Item属性是默认属性在访问元素时可以省略它的名称。 Console.WriteLine("关于键 = \\"rtf\\", 值 = {0}.", openWith["rtf"]);//可以使用默认的Item属性更改该值与键相关联。openWith["rtf"] = "winword.exe";Console.WriteLine("关于键 = \\"rtf\\", 值 = {0}.", openWith["rtf"]);//如果键不存在,设置默认的Item属性为该键添加新的键/值对。openWith["doc"] = "winword.exe";// ContainsKey可用于在插入键之前测试键。 if (!openWith.ContainsKey("ht")){openWith.Add("ht", "hypertrm.exe");Console.WriteLine("键增加值 = \\"ht\\": {0}", openWith["ht"]);}// 当您使用foreach枚举哈希表元素时,元素将作为KeyValuePair对象检索。Console.WriteLine("=====");foreach (DictionaryEntry de in openWith){Console.WriteLine("键 = {0}, 值 = {1}", de.Key, de.Value);}// 要单独获取值,使用values属性。ICollection valueColl = openWith.Values;// ValueCollection 的元素使用为哈希表值指定的类型进行强类型处理。Console.WriteLine("=====");foreach (string s in valueColl){Console.WriteLine("值 = {0}", s);}// 要单独获取密钥,请使用keys属性。ICollection keyColl = openWith.Keys;// KeyCollection的元素是强类型的,与哈希表键指定的类型一致。Console.WriteLine("=====");foreach (string s in keyColl){Console.WriteLine("键 = {0}", s);}// 使用Remove方法删除键/值对。Console.WriteLine("\\n移除(\\"doc\\")");openWith.Remove("doc");if (!openWith.ContainsKey("doc")){Console.WriteLine("键 \\"doc\\" 没有找到");}}
}
运行结果:

在上例中,演示了哈希表的一些基本操作,其中包括但不限于添加元素、根据键添加设置元素值、遍历输出表中的键和值元素、删除指定键的元素以及判断表中是否包含某个元素等。
Hashtable 类一些常用的属性:
| 属性名 | 描述 |
|---|---|
| Count | 获取包含在 Hashtable 中的键/值对的数目。 |
| IsFixedSize | 获取一个值,该值指示 Hashtable 是否具有固定大小。 |
| IsReadOnly | 获取一个值,该值指示 Hashtable 是否为只读。 |
| Item[Object] | 获取或设置与指定的键关联的值。 |
| Keys | 获取包含 ICollection 中的键的 Hashtable。 |
| Values | 获取一个 ICollection,它包含 Hashtable 中的值。 |
Hashtable 类一些常用的方法:
| 方法名 | 描述 |
|---|---|
| Add(Object, Object) | 将带有指定键和值的元素添加到 Hashtable 中。 |
| Clear() | 从 Hashtable 中移除所有元素。 |
| Clone() | 创建 Hashtable 的浅表副本。 |
| Contains(Object) | 确定 Hashtable 是否包含特定键。 |
| ContainsKey(Object) | 确定 Hashtable 是否包含特定键。 |
| ContainsValue(Object) | 确定 Hashtable 是否包含特定值。 |
| CopyTo(Array, Int32) | 将 Hashtable 元素复制到一维 Array 实例中的指定索引位置。 |
| Equals(Object) | 确定指定对象是否等于当前对象。(继承自 Object) |
| GetType() | 获取当前实例的 Type。(继承自 Object) |
| KeyEquals(Object, Object) | 将特定 Object 与 Hashtable 中的特定键进行比较。 |
| Remove(Object) | 从 Hashtable 中移除带有指定键的元素。 |
| ToString() | 返回表示当前对象的字符串。(继承自 Object) |
5、队列(Queue)
队列是一种特殊的线性表2,只在表头(也称为队头)进行删除操作,只在表尾(也称为队尾)进行插入操作。由于队列的修改是按先进先出的原则,所以队列又称为先进先出(First In First Out)表,简称FIFO表。在项目中实现异步任务和进程之间的通信是队列最常用的两个方面。可以概述为不需要马上获得结果,但又需要控制一定的并发量3的时候,就需要用到队列。
示例代码:
using System;
using System.Collections;
public class SamplesQueue
{public static void Main(){// 创建并初始化一个新的队列Queue myQ = new Queue();myQ.Enqueue("Hello");myQ.Enqueue("World");myQ.Enqueue("!");// 展示队列的属性和元素值Console.WriteLine("myQ");Console.WriteLine("元素个数:{0}", myQ.Count);Console.Write("元素值:");PrintValues(myQ);}public static void PrintValues(IEnumerable myCollection){foreach (Object obj in myCollection)Console.Write("{0}", obj);Console.WriteLine();}
}
运行结果:

在上例中演示了如何创建并初始化一个队列对象,可以使用 Enqueue 方法将元素添加到 Queue 队列,并使用循环输出队列中的元素。
Queue 类一些常用的属性:
| 属性名 | 描述 |
|---|---|
| Count | 获取 Queue 中包含的元素数。 |
| SyncRoot | 获取可用于同步对 Queue 的访问的对象。 |
Queue 类一些常用的方法:
| 方法名 | 描述 |
|---|---|
| Clear() | 从 Queue 中移除所有对象。 |
| Clone() | 创建 Queue 的浅表副本。 |
| Contains(Object) | 确定某元素是否在 Queue 中。 |
| CopyTo(Array, Int32) | 从指定数组索引开始将 Queue 元素复制到现有一维 Array 中。 |
| Dequeue() | 移除并返回位于 Queue 开始处的对象。 |
| Enqueue(Object) | 将对象添加到 Queue 的结尾处。 |
| Equals(Object) | 确定指定对象是否等于当前对象。(继承自 Object) |
| GetType() | 获取当前实例的 Type。(继承自 Object) |
| Peek() | 返回位于 Queue 开始处的对象但不将其移除。 |
| ToArray() | 将 Queue 元素复制到新数组。 |
| ToString() | 返回表示当前对象的字符串。(继承自 Object) |
6、排序列表(SortedList)
排序列表是数组和哈希表的组合。其中包含一个可使用键或索引访问各项的列表。当使用索引访问各项时,排序列表是一个动态数组,当使用键访问各项,排序列表是一个哈希表。集合中的各项总是按键值排序。当集合中的元素个数非常大,且需要按照一定的规则进行排序时,就可以考虑使用排序列表。
示例代码:
using System;
using System.Collections;public class SamplesSortedList
{public static void Main(){// 创建并初始化一个新的排序列表 SortedList mySL = new SortedList();mySL.Add("Third", "!");mySL.Add("Second", "World");mySL.Add("First", "Hello");// 显示排序列表的属性和元素值Console.WriteLine("mySL");Console.WriteLine(" 元素个数: {0}", mySL.Count);Console.WriteLine(" 列表容量: {0}", mySL.Capacity);Console.WriteLine(" 键和值:");PrintKeysAndValues(mySL);}public static void PrintKeysAndValues(SortedList myList){Console.WriteLine("\\t-键-\\t-值-");for (int i = 0; i < myList.Count; i++){Console.WriteLine("\\t{0}:\\t{1}", myList.GetKey(i), myList.GetByIndex(i));}Console.WriteLine();}
}
运行结果:

在上例中,我们既可以像哈希表一样使用 Add 方法往集合中添加元素,也可以像数组一样使用索引访问集合元素。
SortedList 类一些常用的属性:
| 属性名 | 描述 |
|---|---|
| Capacity | 获取或设置 SortedList 对象的容量。 |
| Count | 获取 SortedList 对象中包含的元素数。 |
| IsFixedSize | 获取一个值,该值指示 SortedList 对象是否具有固定大小。 |
| IsReadOnly | 获取一个值,该值指示 SortedList 对象是否为只读。 |
| Item[Object] | 获取或设置与 SortedList 对象中的特定键相关联的值。 |
| Keys | 获取 SortedList 对象中的键。 |
| Values | 获取 SortedList 对象中的值。 |
SortedList 类一些常用的方法:
| 方法名 | 描述 |
|---|---|
| Add(Object, Object) | 将带有指定键和值的元素添加到 SortedList 对象。 |
| Clear() | 从 SortedList 对象中移除所有元素。 |
| Clone() | 创建 SortedList 对象的浅表副本。 |
| Contains(Object) | 确定 SortedList 对象是否包含特定键。 |
| ContainsKey(Object) | 确定 SortedList 对象是否包含特定键。 |
| ContainsValue(Object) | 确定 SortedList 对象是否包含特定值。 |
| CopyTo(Array, Int32) | 从指定数组索引开始将 SortedList 元素复制到一维 Array 对象中。 |
| Equals(Object) | 确定指定对象是否等于当前对象。(继承自 Object) |
| GetByIndex(Int32) | 获取 SortedList 对象的指定索引处的值。 |
| GetKey(Int32) | 获取 SortedList 对象的指定索引处的键。 |
| GetKeyList() | 获取 SortedList 对象中的键。 |
| GetType() | 获取当前实例的 Type。(继承自 Object) |
| GetValueList() | 获取 SortedList 对象中的值。 |
| IndexOfKey(Object) | 返回 SortedList 对象中指定键的从零开始的索引。 |
| IndexOfValue(Object) | 返回指定的值在 SortedList 对象中第一个匹配项的从零开始的索引。 |
| Remove(Object) | 从 SortedList 对象中移除带有指定键的元素。 |
| RemoveAt(Int32) | 移除 SortedList 对象的指定索引处的元素。 |
| SetByIndex(Int32, Object) | 替换 SortedList 对象中指定索引处的值。 |
| ToString() | 返回表示当前对象的字符串。(继承自 Object) |
| TrimToSize() | 将容量设置为 SortedList 对象中元素的实际数目。 |
7、堆栈(Stack)
堆栈是限定仅在表尾进行插入或删除操作的一种特殊的线性表。因此,对堆栈来说,表尾端有特殊含义,称为栈顶(top), 表头端成为栈底(bottom),堆栈的修改是按后进先出的原则进行的。 不含元素的空表成为空栈。当数据的处理顺序要与接收顺序相反时(LIFO),我们就可以用堆栈的结构。
示例代码:
using System;
using System.Collections;
public class SamplesStack
{public static void Main(){// 创建并初始化一个新的栈Stack myStack = new Stack();myStack.Push("Hello");myStack.Push("World");myStack.Push("!");// 显示栈的属性和元素值 Console.WriteLine("myStack");Console.WriteLine("\\t元素个数:{0}", myStack.Count);Console.Write("\\t元素值:");PrintValues(myStack);}public static void PrintValues(IEnumerable myCollection){foreach (Object obj in myCollection)Console.Write("{0}", obj);Console.WriteLine();}
}
运行结果:

在上例中可以发现最先进入到栈内的元素反而最晚出来,遵循着先进后出的原则。这刚好与队列先进先出的表现相反。
Stack 类一些常用的属性:
| 属性名 | 描述 |
|---|---|
| Count | 获取 Stack 中包含的元素数。 |
| IsSynchronized | 获取一个值,该值指示是否同步对 Stack 的访问(线程安全)。 |
| SyncRoot | 获取可用于同步对 Stack 的访问的对象。 |
Stack 类一些常用的方法:
| 方法名 | 描述 |
|---|---|
| Clear() | 从 Stack 中移除所有对象。 |
| Clone() | 创建 Stack 的浅表副本。 |
| Contains(Object) | 确定某元素是否在 Stack 中。 |
| CopyTo(Array, Int32) | 从指定的数组索引处开始,将 Stack 复制到现有的一维 Array 中。 |
| Equals(Object) | 确定指定对象是否等于当前对象。(继承自 Object) |
| GetType() | 获取当前实例的 Type。(继承自 Object) |
| Peek() | 返回位于 Stack 顶部的对象但不将其移除。 |
| Pop() | 删除并返回 Stack 顶部的对象。 |
| Push(Object) | 在 Stack 的顶部插入一个对象。 |
| ToArray() | 将 Stack 复制到新数组中。 |
| ToString() | 返回表示当前对象的字符串。(继承自 Object) |
点击了解更多集合类的使用。
结语
🌅 以上就是 C# 集合类的介绍啦,希望对大家有所帮助。感谢大家的支持。
-
哈希码: 是一种并不完全唯一的算法,让同一个类的对象按照自己不同的特征尽量的有不同的哈希码,但并不表示不同的对象哈希码完全不同。也有相同的情况,看卡法这如何写哈希码的算法。 ↩︎
-
线性表: 表示 n 个具有相同特性的数据元素的有限序列。是最简单、最基本、也是最常用的一种数据结构。 ↩︎
-
并发量: 指的是系统同时处理的操作或请求数量。 ↩︎


