在C#的日常开发中,泛型集合几乎是每个项目都会用到的东西。相比早期的ArrayList和Hashtable,泛型集合List<T>和Dictionary<TKey, TValue>在类型安全和性能上都有明显优势。本文将系统地讲解这两个集合的用法,包括创建、增删改查、遍历、排序以及使用中的注意事项,帮助你彻底掌握这个必会知识点。

为什么使用泛型集合而不是非泛型集合
在泛型出现之前,.NET提供了ArrayList这样的集合类。ArrayList内部存储的是object类型,往里面添加任何值类型的数据都会发生装箱操作,读取时又要拆箱,这两步操作都会带来性能损耗。更麻烦的是类型安全问题:你可以往同一个ArrayList里既放int又放string,编译器不会报错,但取出来使用时一旦类型转换失败,程序就会在运行时抛出InvalidCastException。
泛型集合的出现就是为了解决这两个问题。List<int>在声明时就明确了元素类型,编译器会强制检查所有添加进来的元素,类型不匹配直接编译不通过,把潜在的运行时错误提前到了编码阶段。同时由于类型确定,值类型不再需要装箱拆箱,在大量数据的场景下性能差距可以达到数倍。因此现在开发中一律推荐使用System.Collections.Generic命名空间下的泛型集合。
List<T>的常用操作详解
List<T>是最常用的有序集合,底层基于数组实现,支持通过下标快速访问。先看一组基础操作代码:
List<string> names = new List<string>();
// 添加元素
names.Add("张三");
names.AddRange(new[] { "李四", "王五" });
// 插入元素到指定位置
names.Insert(0, "赵六");
// 判断是否包含某个元素
bool exists = names.Contains("张三");
// 查找元素下标
int index = names.IndexOf("李四");
// 移除元素
names.Remove("张三");
// 移除指定下标的元素
names.RemoveAt(0);
// 获取元素个数
int count = names.Count;
// 下标访问
string first = names[0];遍历List有两种常见方式,一种是for循环配合下标,一种是foreach。如果遍历过程中不需要修改集合,推荐用foreach,代码更简洁;如果需要按下标操作或者遍历中要移除元素,则要用for循环并注意倒序遍历。需要特别提醒的是,在foreach循环中直接对集合做Add或Remove操作会抛出InvalidOperationException异常,这是初学者非常容易踩的坑。如果要边遍历边筛选,可以先使用FindAll返回新集合,或者用倒序的for循环删除。
排序方面,可以调用Sort方法配合lambda表达式,也可以使用LINQ的OrderBy。两者的区别在于Sort是在原集合上就地排序,而OrderBy返回的是新的排序结果,原集合顺序不变。对于自定义类型,还可以让类实现IComparable<T>接口,或者在Sort时传入Comparison委托:
List<int> nums = new List<int> { 5, 2, 8, 1 };
nums.Sort(); // 升序:1 2 5 8
nums.Sort((a, b) => b.CompareTo(a)); // 降序:8 5 2 1
nums.Reverse(); // 反转Dictionary<TKey, TValue>的用法与技巧
Dictionary也就是常说的字典或哈希表,以键值对的形式存储数据,底层基于哈希算法实现,通过key查找value的平均时间复杂度接近O(1),非常适合需要频繁根据某个标识快速定位数据的场景。基础用法如下:
Dictionary<int, string> userDict = new Dictionary<int, string>(); // 添加键值对 userDict.Add(1, "管理员"); userDict[2] = "普通用户"; // 下标方式添加或覆盖 // 读取,key不存在会抛异常 string role = userDict[1]; // 安全读取,key不存在返回默认值 userDict.TryGetValue(3, out string value); // 判断是否包含某个key bool hasKey = userDict.ContainsKey(2); // 移除 userDict.Remove(1); // 个数 int n = userDict.Count;
使用Dictionary有几个高频踩坑点需要记住。第一,key必须唯一,对已存在的key调用Add会抛出ArgumentException异常,但用下标赋值的方式是安全的,它会直接覆盖旧值。第二,直接用下标读取一个不存在的key会抛出KeyNotFoundException,所以在不确定key是否存在时,应该优先使用TryGetValue方法,它一次哈希查找就能同时完成判断和取值,比先ContainsKey再取值的写法效率更高。第三,作为key的类型如果是我们自定义的类,需要正确重写GetHashCode和Equals方法,否则默认按引用比较,两个内容相同的对象会被当成不同的key。
遍历字典时,可以遍历KeyValuePair结构,也可以分别遍历Keys和Values属性。如果遍历过程中需要修改字典,同样需要先把要处理的数据缓存到临时列表中再操作:
foreach (KeyValuePair<int, string> kv in userDict)
{
Console.WriteLine($"key={kv.Key}, value={kv.Value}");
}
foreach (var key in userDict.Keys)
{
Console.WriteLine(key);
}如何选择以及性能对比
List和Dictionary各有适用场景,核心判断依据是你要按什么方式访问数据。如果数据是有序的一批记录,主要做顺序遍历、按下标访问或者尾部追加,用List更合适;如果数据是一对一的映射关系,需要频繁根据key快速查找,用Dictionary明显更快。举个典型例子:有一万条用户数据,要根据用户ID反复查找用户信息,用List每次查找平均要比较五千次,而用Dictionary只需一次哈希定位,性能差距在这种数据量下会非常直观。
另外提两点实践建议。一是集合初始化时如果能预估数据量,可以通过构造函数传入初始容量,比如new List<string>(1000),这样可以减少内部数组自动扩容带来的复制开销。二是在多线程环境下,List和Dictionary都不是线程安全的,并发读写应使用System.Collections.Concurrent命名空间下的ConcurrentDictionary等类型,或者用锁加以保护。掌握这些要点后,遇到集合选型和性能问题就能从容应对了。
C#泛型集合List用法Dictionary用法修改时间:2026-09-13 20:50:42