C# 集合 (Collections) 详解
在 C# 中,集合用于存储和管理对象组。根据是否支持泛型(Generic),主要分为两大类:
- 泛型集合 (``System.Collections.Generic``): 安全、高效,推荐使用。如 ``List<T>``, ``Dictionary<TKey, TValue>``。
- 非泛型集合 (``System.Collections``): 存储 ``object`` 类型,涉及装箱拆箱,性能较差,主要用于兼容旧代码。如 ``ArrayList``, ``Hashtable``。
1. List<T> (泛型列表)
``List<T>`` 是 C# 中最常用的集合,它本质上是一个大小可变的数组。
1.1 引用命名空间
在使用 List 之前,必须引用以下命名空间:
using System.Collections.Generic;
1.2 定义与初始化
声明 List 时必须指定数据类型(T)。
// 1. 声明并实例化一个空的 List List<int> list1 = new List<int>(); // 2. 使用集合初始化器 (声明时直接赋值) var list2 = new List<int>() { 1, 2, 3, 5, 8, 2, 4 }; // 3. 添加元素演示 List<int> list = new List<int>(); list.Add(2); // 索引 0 list.Add(2); // 索引 1 list.Add(3); // 索引 2 // 4. 读取与计算 int k = 8; int b; Console.WriteLine(list[2] + k); // 输出 11 (3 + 8) Console.WriteLine(b = list[0]); // 输出 2 Console.WriteLine(b = list[0] + list[1]); // 输出 4 (2 + 2)
1.3 常用属性与方法
假设 ``var list = new List<int>();``
| 属性/方法 | 描述 | 示例代码 |
|---|---|---|
| Count | 获取实际包含的元素个数 | ``int c = list.Count;`` |
| Capacity | 获取内部数据结构的容量(通常大于等于 Count) | ``int cap = list.Capacity;`` |
| Add | 在列表末尾添加一个元素 | ``list.Add(100);`` |
| Contains | 判断元素是否存在,返回 bool | ``bool has5 = list.Contains(5);`` |
| Clear | 清空所有元素 | ``list.Clear();`` |
| Sort | 对元素进行升序排列 | ``list.Sort();`` |
| Reverse | 反转列表中元素的顺序 | ``list.Reverse();`` |
| Remove | 移除第一个匹配的特定对象 | ``list.Remove(5);`` (删掉第一个5) |
| RemoveAt | 移除指定索引处的元素 | ``list.RemoveAt(0);`` (删掉第1个元素) |
| RemoveRange | 移除一定范围的元素 (起始索引, 个数) | ``list.RemoveRange(0, 3);`` (删掉前3个) |
| Insert | 在指定索引处插入一个元素 | ``list.Insert(1, 125);`` (在索引1前插) |
| InsertRange | 在指定索引处插入一个集合 | ``list.InsertRange(1, new int[]{1,2});`` |
逻辑示例:安全添加
if (!list.Contains(5)) // 如果列表中没有 5 { list.Add(5); // 则添加 5 } else { // MessageBox.Show("已经有了"); // WinForms 环境 Console.WriteLine("已经有了"); }
1.4 遍历 List
方式 A:foreach 循环 (推荐)
List<string> list_data = new List<string> { "A", "B", "C" }; // 假设在 WinForms 中有一个 listBox1 foreach (string item in list_data) { // listBox1.Items.Add(item); Console.WriteLine(item); }
1.5 实战:计算平均值
static void Main(string[] args) { var list = new List<int>(); list.Add(8); list.Add(2); list.Add(3); double avg = CalculateAverage(list); Console.WriteLine("平均值: " + avg); } static double CalculateAverage(List<int> kar) { double sum = 0; foreach (var item in kar) { sum = sum + item; } // 注意:kar.Count 是 int,sum 是 double,除法结果为 double if (kar.Count == 0) return 0; return sum / kar.Count; }
2. ArrayList (动态数组 - 旧版)
``ArrayList`` 属于 ``System.Collections`` 命名空间。它是非泛型的,可以存储任意类型的元素(混合存储),但在现代开发中不推荐使用,因为它存在装箱拆箱的性能损耗且类型不安全。
2.1 创建对象
using System.Collections; // 必须引用 // 1. 创建空对象 ArrayList listdd = new ArrayList(); listdd.Add("Hello"); listdd.Add(123); // 混合类型 // 2. 使用构造函数从现有数组创建 int[] reey = new int[] { 1, 2, 3, 4, 5, 6 }; ArrayList list = new ArrayList(reey);
2.2 属性与方法
用法与 ``List<T>`` 极其相似,但参数通常是 ``object``。 * ``Add(object value)`` * ``Insert(int index, object value)`` * ``Remove(object obj)``
3. Hashtable (哈希表 - 旧版)
``Hashtable`` 用于存储键值对 (Key-Value)。它也是非泛型的,Key 和 Value 都是 ``object`` 类型。
3.1 定义与添加
Hashtable HaXi = new Hashtable(); // 方式 1:使用 Add 方法 HaXi.Add(1, 2); HaXi.Add(2, 2.56); HaXi.Add(3, false); HaXi.Add(4, "你好"); HaXi.Add("ID", "BH0021"); // Key 也可以是字符串 // 方式 2:使用索引器 (如果 Key 不存在则添加,存在则替换/更新) HaXi[7] = 2.56; HaXi[1] = 99; // 将 Key 为 1 的值更新为 99
3.2 常用操作
// 移除 HaXi.Remove(6); // 根据 Key 移除 // 判断 Key 是否存在 if (!HaXi.ContainsKey(2)) { HaXi[2] = 9888; } // 判断 Value 是否存在 bool hasValue = HaXi.ContainsValue("你好"); // 清空 HaXi.Clear();
3.3 遍历 Hashtable
由于 Hashtable 是无序的,通常遍历 ``Keys`` 或 ``Values``,或者遍历 ``DictionaryEntry``。
// 遍历所有的 Key foreach (var key in HaXi.Keys) { // 通过 Key 获取 Value string content = "Key: " + key + " Value: " + HaXi[key]; Console.WriteLine(content); }
3.4 数组转 Hashtable
int[] reey = new int[] { 1, 2, 3, 4, 5, 6 }; Hashtable haxi = new Hashtable(); // 将数组索引作为 Key,数组元素作为 Value for (int i = 0; i < reey.Length; i++) { haxi[i] = reey[i]; }
4. Dictionary<TKey, TValue> (泛型字典)
``Dictionary`` 是现代 C# 开发中存储键值对的首选。它比 Hashtable 更快、更安全。
4.1 定义与初始化
// 键是 int,值是 string Dictionary<int, string> dict = new Dictionary<int, string>(); // 初始化器语法 var dict2 = new Dictionary<int, string>() { { 1, "One" }, { 2, "Two" } };
4.2 增删改查
// 1. 添加 (Add 方法如果 Key 已存在会报错) dict.Add(3, "Three"); // 2. 更新/添加 (索引器更安全,不存在则添加,存在则覆盖) dict[1] = "New Value"; // 3. 删除 dict.Remove(1); // 删除 Key 为 1 的项 dict.Clear(); // 清空 // 4. 安全查找 (重要!) // 直接访问 dict[99] 如果 Key 不存在会抛出异常 if (dict.ContainsKey(1)) { Console.WriteLine(dict[1]); } // 推荐:TryGetValue (尝试获取,返回 bool,值存入 out 参数) string value; if (dict.TryGetValue(1, out value)) { Console.WriteLine("获取成功: " + value); } else { Console.WriteLine("Key 不存在"); }
4.3 遍历 Dictionary
// 1. 遍历键值对 (KeyValuePair) foreach (KeyValuePair<int, string> pair in dict) { Console.WriteLine("Key: " + pair.Key + ", Value: " + pair.Value); } // 2. 仅遍历键 foreach (int key in dict.Keys) { /*...*/ } // 3. 仅遍历值 foreach (string val in dict.Values) { /*...*/ }
5. 对比与总结
5.1 Hashtable vs Dictionary
| 特性 | Hashtable | Dictionary<TKey, TValue> |
|---|---|---|
| 类型安全 | 低 (非泛型,存取需类型转换) | 高 (泛型,编译时检查类型) |
| 性能 | 较差 (涉及装箱拆箱) | 优异 (无装箱,且无需同步开销) |
| 线程安全 | 是 (内部支持多线程同步,较慢) | 否 (默认非线程安全,需手动锁) |
| 命名空间 | ``System.Collections`` | ``System.Collections.Generic`` |
| 推荐场景 | 维护旧遗留代码 | 现代 C# 开发首选 |
5.2 扩展:ConcurrentDictionary
如果在多线程环境下需要频繁操作字典,推荐使用 ``ConcurrentDictionary``。
* 命名空间: ``System.Collections.Concurrent`` * 特点: 它是线程安全的字典实现,不需要手动加锁 (lock),性能优于手动锁定的 Dictionary。
using System.Collections.Concurrent; ConcurrentDictionary<int, string> conDict = new ConcurrentDictionary<int, string>(); conDict.TryAdd(1, "Value1"); // 线程安全的添加