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 ) ;