C#基础13-泛型集合
1、List(动态数组)
(1)核心特性与优势
类型安全
强制元素类型一致(如 List<int> 只存整数),避免 ArrayList 的装箱拆箱开销。 编译时类型检查,减少运行时错误。 动态扩容
初始容量默认为 4,添加元素超限时自动倍增容量(如 4→8→16)。 可通过 Capacity 属性查看当前容量,Count 获取实际元素数。 高效内存管理
连续内存存储,支持快速索引访问(时间复杂度 O(1))。 对比数组:无需手动扩容,对比 ArrayList:避免值类型装箱。
(2)基础操作(增删查改)
List< string > names = new List< string > ( ) ;
names. Add ( "Alice" ) ;
string [ ] newNames = { "Bob" , "Charlie" } ;
names. AddRange ( newNames) ;
names. Insert ( 1 , "David" ) ;
names. Remove ( "Alice" ) ;
names. RemoveAt ( 0 ) ;
names. RemoveRange ( 1 , 2 ) ;
names. Clear ( ) ;
bool hasBob = names. Contains ( "Bob" ) ;
int index = names. IndexOf ( "Charlie" ) ;
string first = names[ 0 ] ;
(3)进阶操作与高阶方法
List< int > numbers = new List< int > { 1 , 2 , 3 , 4 , 5 } ;
List< int > evens = numbers. FindAll ( n => n % 2 == 0 ) ;
- `ConvertAll`:类型转换(如 `int` → `string`)
List< string > numStrings = numbers. ConvertAll ( n => n. ToString ( ) ) ;
int target = numbers. Find ( n => n > 3 ) ;
bool allPositive = numbers. TrueForAll ( n => n > 0 ) ;
numbers. Sort ( ) ;
numbers. Reverse ( ) ;
numbers. Sort ( ( a, b) => b. CompareTo ( a) ) ;
(4)性能优化与避坑指南
预设容量:提前设置 Capacity 避免频繁扩容:
List< int > bigList = new List< int > ( 1000 ) ;
避免遍历中修改集合:使用 for 循环替代 foreach 删除元素,防止 InvalidOperationException。 值类型集合优化:对大量结构体(struct)使用 List<ValueType> 减少堆内存分配。 批量操作优先:用 AddRange() 替代多次 Add(),减少扩容次数。
(5)典型应用场景
数据绑定:作为 DataGridView 数据源:
dataGridView. DataSource = new BindingList< Person> ( personList) ;
API 数据处理:反序列化 JSON 数组 → List<T>:
List< User> users = JsonConvert. DeserializeObject < List< User> > ( json) ;
高效集合运算:使用 LINQ 扩展方法(需 using System.Linq):
var union = list1. Union ( list2) . ToList ( ) ;
var difference = list1. Except ( list2) ;
2、Dictionary<TKey, TValue>(键值对字典)
(1)核心特性与优势
哈希表实现
基于哈希表存储键值对,提供接近 O(1) 的查找效率(性能取决于键的哈希算法质量)。 示例:Dictionary<string, int> 比遍历 List 查找快数十倍。 类型安全与强类型约束
键 (TKey) 和值 (TValue) 均为泛型,编译时检查类型错误,避免 Hashtable 的装箱拆箱开销。 动态扩容机制
初始容量默认为 0,首次添加元素时扩容至 4,后续按指数级增长(4→8→16)。 通过 Capacity 属性预设容量减少扩容开销。
(2)基础操作与语法
var scores = new Dictionary< string , int > ( ) ;
scores. Add ( "Alice" , 90 ) ;
scores[ "Bob" ] = 85 ;
scores. Remove ( "Alice" ) ;
scores[ "Bob" ] = 95 ;
if ( scores. TryGetValue ( "Bob" , out int score) )
{
Console. WriteLine ( score) ;
}
注意:直接通过 scores["Unknown"] 访问不存在的键会抛出异常。 键值约束
键 (TKey):不可为 null,且对象作为键时禁止修改影响哈希值的属性。 值 (TValue):可为 null(若 TValue 是引用类型)。
(3)遍历字典的四种方式
方法 语法示例 适用场景 遍历 KeyValuePair foreach (var kvp in dict) { kvp.Key }需同时访问键和值(最常用) 单独遍历键集合 foreach (var key in dict.Keys) { }仅需处理键(如批量删除) 单独遍历值集合 foreach (var value in dict.Values) { }仅需统计或处理值 通过索引访问键 var key = dict.ElementAt(0).Key;需按插入顺序访问(不推荐)
提示:Dictionary 的元素顺序与添加顺序无关,需有序存储请改用 SortedDictionary。
(4)高级应用场景
数据缓存:利用 O(1) 查询特性存储频繁访问的数据(如数据库查询结果):
private static Dictionary< int , Product> _productCache = new ( ) ;
public Product GetProduct ( int id)
{
if ( ! _productCache. TryGetValue ( id, out var product) )
{
product = FetchFromDatabase ( id) ;
_productCache. Add ( id, product) ;
}
return product;
}
配置项管理:存储应用程序配置(如环境变量、用户设置):
var configs = new Dictionary< string , string >
{
[ "Theme" ] = "Dark" ,
[ "Timeout" ] = "30"
} ;
var words = new List< string > { "apple" , "banana" , "apple" } ;
var countDict = new Dictionary< string , int > ( ) ;
foreach ( var word in words)
{
countDict[ word] = countDict. TryGetValue ( word, out int count) ? count + 1 : 1 ;
}
(5)性能优化与陷阱规避
预设初始容量:已知元素数量时,构造器指定容量避免多次扩容:var dict = new Dictionary<string, int>(capacity: 1000);。 避免修改键对象:若键对象被修改导致哈希值变化,该键将无法被检索到。 线程安全问题:Dictionary 非线程安全,多线程环境需改用 ConcurrentDictionary 或手动加锁。 优先使用 TryGetValue:比先 ContainsKey 再取值减少一次哈希计算。
(6)与相似类型的对比
集合类型 特点 适用场景 Dictionary哈希表实现,查询极快 高频键值查找、缓存 SortedDictionary红黑树实现,按键排序 需有序遍历键值对 Hashtable非泛型,需装箱拆箱 遗留代码兼容(不推荐新项目) ConcurrentDictionary线程安全版本 多线程共享字典
3、Queue(先进先出队列)
(1)核心特性
先进先出(FIFO)
元素从队尾入队(Enqueue),从队头出队(Dequeue),确保最早加入的元素最先被处理。 示例:
Queue< string > queue = new Queue< string > ( ) ;
queue. Enqueue ( "First" ) ;
queue. Enqueue ( "Second" ) ;
string firstItem = queue. Dequeue ( ) ;
线程安全性
非线程安全:默认实现不支持多线程并发操作,需手动加锁或使用 ConcurrentQueue<T>。 线程安全替代方案:
ConcurrentQueue< int > concurrentQueue = new ConcurrentQueue< int > ( ) ;
concurrentQueue. Enqueue ( 100 ) ;
允许重复值与 null
可存储重复元素,且接受 null 作为有效值(若 T 为引用类型)。
(2)基础操作与方法
方法/属性 作用 示例 Enqueue(T item)添加元素到队尾 queue.Enqueue("NewItem");Dequeue()移除并返回队头元素(队列为空时抛异常) string item = queue.Dequeue();Peek()查看队头元素但不移除 string head = queue.Peek();Count获取队列中元素数量 int num = queue.Count;Contains(T item)检查元素是否存在 bool exists = queue.Contains("A");Clear()清空队列 queue.Clear();ToArray()将队列转为数组(顺序:队头→队尾) string[] arr = queue.ToArray();
(3)使用注意事项
容量与性能优化
默认初始容量为 4,扩容时容量翻倍(2×)。可通过构造函数指定初始容量减少扩容开销:
Queue< int > optimizedQueue = new Queue< int > ( 100 ) ;
- 调用 `TrimExcess()` 释放多余内存(当元素数 < 容量的90%时有效)。
避免空队列操作:调用 Dequeue() 或 Peek() 前需检查队列是否为空,否则抛出 InvalidOperationException:
if ( queue. Count > 0 ) {
var item = queue. Dequeue ( ) ;
}
(4)典型应用场景
任务调度系统:按提交顺序处理任务(如订单处理、日志消费):
Queue< Action> taskQueue = new Queue< Action> ( ) ;
taskQueue. Enqueue ( ( ) => Console. WriteLine ( "Task 1" ) ) ;
taskQueue. Enqueue ( ( ) => Console. WriteLine ( "Task 2" ) ) ;
while ( taskQueue. Count > 0 ) {
taskQueue. Dequeue ( ) . Invoke ( ) ;
}
广度优先搜索(BFS):遍历树或图结构时,用队列管理待访问节点:
Queue< TreeNode> nodes = new Queue< TreeNode> ( ) ;
nodes. Enqueue ( rootNode) ;
while ( nodes. Count > 0 ) {
var current = nodes. Dequeue ( ) ;
}
消息缓冲机制:在多线程环境中,用 ConcurrentQueue<T> 实现生产者-消费者模型:
concurrentQueue. Enqueue ( newMessage) ;
if ( concurrentQueue. TryDequeue ( out var msg) ) {
ProcessMessage ( msg) ;
}
(5)与其他集合对比
集合类型 顺序 允许重复 线程安全 适用场景 Queue<T>FIFO ✅ ❌(需手动同步) 顺序任务处理、BFS Stack<T>LIFO ✅ ❌ 撤销操作、递归算法 ConcurrentQueue<T>FIFO ✅ ✅ 高并发生产者-消费者模型 List<T>索引 ✅ ❌ 随机访问、动态数组需求
4、Stack(后进先出栈)
(1)核心特性
后进先出(LIFO)结构:元素按插入顺序反向处理,最后添加的元素最先被移除。
(2)基础操作与方法
Stack< int > stack = new Stack< int > ( ) ;
Stack< string > stack2 = new Stack< string > ( 10 ) ;
stack. Push ( 1 ) ;
stack. Push ( 2 ) ;
Pop():移除并返回栈顶元素(栈空时抛 InvalidOperationException)。
int top = stack. Pop ( ) ;
int currentTop = stack. Peek ( ) ;
TryPop(out T result) & TryPeek(out T result):安全操作,避免异常。
if ( stack. TryPop ( out int value ) )
{
Console. WriteLine ( $"弹出元素: { value } " ) ;
}
(3)进阶用法与实践技巧
foreach ( var item in stack)
{
Console. WriteLine ( item) ;
}
var reversed = stack. ToArray ( ) . Reverse ( ) ;
资源清理与容量优化
手动释放资源:结合 Clear() 清空栈元素。 减少内存占用:调用 TrimExcess() 释放未用空间(当元素数小于容量的90%时生效)。 线程安全场景
多线程环境下使用 ConcurrentStack<T>:
var concurrentStack = new ConcurrentStack< int > ( ) ;
concurrentStack. Push ( 10 ) ;
concurrentStack. TryPop ( out int result) ;
(4)性能与底层机制
时间复杂度
Push() 和 Pop() 操作在未触发扩容时为 O(1);扩容时(容量翻倍)为 O(n)。Peek() 始终为 O(1)。 内部实现
基于动态数组存储,自动扩容策略为当前容量的 2 倍。 初始容量过小可能导致频繁扩容,建议预估规模初始化。 对比非泛型 Stack
类型安全:避免装箱拆箱(值类型)和强制转换风险。 性能提升:泛型版本减少运行时开销。
(5)典型应用场景
Stack< ICommand> _undoStack = new Stack< ICommand> ( ) ;
Stack< ICommand> _redoStack = new Stack< ICommand> ( ) ;
public void Execute ( ICommand cmd)
{
cmd. Execute ( ) ;
_undoStack. Push ( cmd) ;
_redoStack. Clear ( ) ;
}
public void Undo ( )
{
if ( _undoStack. TryPop ( out ICommand cmd) )
{
cmd. Undo ( ) ;
_redoStack. Push ( cmd) ;
}
}
Stack< TreeNode> stack = new Stack< TreeNode> ( ) ;
stack. Push ( rootNode) ;
while ( stack. Count > 0 )
{
var node = stack. Pop ( ) ;
Process ( node) ;
foreach ( var child in node. Children. Reverse ( ) )
{
stack. Push ( child) ;
}
}
(6)注意事项
空栈处理:调用 Pop() 或 Peek() 前检查 Count 属性,或使用 TryPop/TryPeek 方法。
if ( stack. Count > 0 )
{
var item = stack. Peek ( ) ;
}
允许 null 值:引用类型栈可插入 null,需注意空值判断:
Stack< string > strStack = new Stack< string > ( ) ;
strStack. Push ( null ) ;
5、HashSet(无序唯一集合)
(1)核心特性与底层机制
唯一性与无序性
元素唯一:自动过滤重复项(依赖 Equals() 和 GetHashCode() 的正确实现)。 无序存储:基于哈希表实现,不保证元素顺序(与 List<T> 的顺序存储对比鲜明)。 高效操作
时间复杂度:
Add()/Remove()/Contains():平均 O(1)(哈希冲突时退化至 O(n))。对比 List<T> 的 Contains() 需遍历(O(n)),大数据量时性能差距显著。 动态扩容:容量翻倍策略,预初始化可优化性能(如 new HashSet<T>(capacity))。 允许 null 值
️(2)核心方法
var emails = new HashSet< string > ( ) ;
emails. Add ( "user@example.com" ) ;
emails. Add ( "user@example.com" ) ;
bool exists = emails. Contains ( "user@example.com" ) ;
emails. Remove ( "user@example.com" ) ;
方法 作用 示例 UnionWith()并集(合并集合) set1.UnionWith(set2);IntersectWith()交集(共同元素) set1.IntersectWith(set2);ExceptWith()差集(移除共有元素) set1.ExceptWith(set2);SymmetricExceptWith()对称差集(独有元素) set1.SymmetricExceptWith(set2);
(3)典型场景
数据去重:替代 List<T> 的遍历去重,效率更高。
int [ ] numbers = { 1 , 2 , 2 , 3 , 4 } ;
var uniqueSet = new HashSet< int > ( numbers) ;
快速检索:如用户邮箱唯一性校验(10万级数据比 List<T> 快百倍):
if ( emailSet. Contains ( inputEmail) )
内存数据库缓存:高频访问数据用 HashSet 缓存,减少数据库查询(需定时刷新防回收)。
(4)与 List<T> 的关键对比
维度 HashSet<T>List<T>唯一性 ✅ 强制唯一 ❌ 允许重复 顺序性 ❌ 无序 ✅ 保留插入顺序 查找性能 O(1)(哈希直接定位) 🐢 O(n)(需遍历) 内存占用 更低(无重复预留空间) 更高(可能预留多余容量) 适用场景 去重、集合运算、高频检索 顺序访问、索引操作、允许重复
️(5)注意事项与进阶技巧
线程安全:非线程安全!多线程环境需用 ConcurrentDictionary<T, byte> 或加锁。 哈希冲突优化
重写 GetHashCode() 确保散列均匀(避免大量冲突导致性能下降)。 内存回收
调用 TrimExcess() 释放未用空间(元素数 < 容量的 90% 时生效)。 与 LINQ 结合
支持 LINQ 操作,但部分方法(如 Distinct())在 HashSet 中冗余:
var evenNumbers = numberSet. Where ( n => n % 2 == 0 ) ;