C#零基础通关第十篇:吃透集合底层原理,搞定List、Dictionary选型与性能优化
上一篇我们彻底掌握了泛型理解了参数化类型、类型安全、泛型约束等高阶核心语法终于明白为什么 List、DictionaryTKey,TValue 能做到一套代码适配所有类型、高性能零装箱。而本篇我们就基于泛型基础拆解C#开发中使用率100%的核心技能——集合。数组、List、ArrayList、Dictionary、Hashtable几乎所有项目的数据存储、批量处理、数据映射全部依赖集合实现。但90%的新手只会无脑 new List根本不懂为什么数组长度固定List 可以动态扩容List 和 ArrayList 到底差在哪频繁查询数据为什么优先用 Dictionary不同集合如何选型、如何规避性能坑本篇从零拆解集合底层原理、扩容机制、性能差异、实战选型彻底告别瞎用集合写出高性能、规范的批量数据处理代码。一、前置认知数组与集合的本质区别1. 数组Array的致命短板我们最早学的数组存在两个无法规避的硬伤也是集合诞生的核心原因长度固定初始化必须指定长度一旦定义无法扩容、缩容数据多了存不下、数据少了浪费内存功能简陋仅支持赋值、取值没有自带增删、查找、排序、去重等批量操作方法在项目动态数据场景用户列表、订单数据、实时批量数据中数组完全无法适配业务需求。2. 集合Collection核心价值C# 集合是基于数组封装的高级数据容器底层依然是数组但封装了动态扩容、增删改查、筛选遍历等全套方法数组是底层载体集合是功能强化版的智能数组。二、非泛型集合老式集合避坑必看C# 集合分为非泛型集合和泛型集合非泛型是早期遗留写法项目中禁止使用但必须了解其缺陷看懂新旧集合的迭代逻辑。1. ArrayList 与 Hashtable这是 .NET 早期的动态集合没有泛型加持所有数据全部以object类型存储。usingSystem;usingSystem.Collections;classOldCollectionDemo{staticvoidMain(){// 非泛型动态数组ArrayListarrayListnewArrayList();arrayList.Add(100);arrayList.Add(C#教程);arrayList.Add(3.14);// 非泛型键值对集合HashtablehtnewHashtable();ht.Add(name,张三);ht.Add(age,18);}}2. 两大致命缺陷项目禁用原因类型不安全可以随意存放任意类型数据编译无报错运行极易出现类型转换异常严重性能损耗值类型存入集合会发生装箱取出使用需要拆箱高频操作性能暴跌结论新项目绝对不要使用 ArrayList、Hashtable全部替换为泛型集合。三、核心重点List 泛型列表项目首选List 是目前使用率最高的集合属于泛型动态数组完美解决数组固定长度、非泛型集合不安全的问题。1. List 核心优势类型安全泛型约束类型只能存入指定类型数据编译校验无运行类型报错零装箱拆箱直接存储指定类型无需类型转换性能极高动态扩容无需手动管理长度数据满了自动扩容方法丰富自带增删、查找、排序、去重、遍历全套工具方法。2. List 常用实战代码usingSystem;usingSystem.Collections.Generic;classListDemo{staticvoidMain(){// 定义int类型泛型列表只能存储int数据ListintnumListnewListint();// 新增数据numList.Add(10);numList.Add(20);numList.AddRange(newint[]{30,40});// 删除数据numList.Remove(20);// 删除指定元素numList.RemoveAt(0);// 删除指定下标// 查询数据boolhasNumnumList.Contains(30);intindexnumList.IndexOf(30);// 遍历集合foreach(variteminnumList){Console.WriteLine(item);}}}3. List 底层扩容机制面试必考很多人以为 List 是无限变长的本质是底层数组替换List 初始化默认容量为0首次添加数据自动开辟4个长度的数组当存储数据达到容量上限时会自动新建一个2倍容量的新数组将旧数组所有数据复制到新数组然后废弃旧数组完成扩容优化技巧如果提前知道数据量级可使用Capacity预定义容量减少多次扩容、数组复制的性能损耗。// 提前预设容量避免多次动态扩容ListintlistnewListint(){Capacity1000};四、核心重点DictionaryTKey,TValue 字典集合Dictionary 是C#的键值对泛型集合对应老式 Hashtable是高性能查找、映射存储的核心容器项目中用于存储一一对应关系的数据。1. Dictionary 核心特性键值唯一TKey 键不允许重复重复添加会直接报错查询极速基于哈希表算法查询时间复杂度为 O(1)数据量越大对比 List 查询优势越明显泛型安全指定键值类型无装箱拆箱性能远超 Hashtable。2. Dictionary 实战常用写法usingSystem;usingSystem.Collections.Generic;classDictionaryDemo{staticvoidMain(){// 键学生ID值学生姓名Dictionaryint,stringstuDicnewDictionaryint,string();// 添加键值对stuDic.Add(1001,张三);stuDic.Add(1002,李四);// 安全取值避免键不存在报错if(stuDic.TryGetValue(1001,outstringname)){Console.WriteLine(查询结果name);}// 遍历字典同时获取键和值foreach(variteminstuDic){Console.WriteLine($ID{item.Key}姓名{item.Value});}}}3. Dictionary 核心使用场景需要根据唯一标识快速查询数据ID查名称、编号查信息存储一一对应的映射关系数据高频查询、低频遍历的业务场景。五、List 与 Dictionary 终极选型对比开发必背新手最容易纠结的问题到底什么时候用 List什么时候用 Dictionary场景需求优先选择核心原因批量存储、遍历、排序、筛选List有序存储遍历效率高方法丰富适合批量处理根据唯一键高频查询数据Dictionary哈希寻址查询速度碾压 List大数据量性能差距极大数据允许重复、无需唯一标识ListDictionary 键唯一不适合重复数据存储键值映射、一对一关联数据Dictionary天然适配键值绑定关系无需手动匹配六、拓展常用集合项目高频辅助1. Queue 队列先进先出遵循先进先出规则先存入的数据先取出适合消息队列、任务排队、日志处理场景。QueuestringqueuenewQueuestring();queue.Enqueue(任务1);queue.Enqueue(任务2);// 取出最先进入的任务stringtaskqueue.Dequeue();2. Stack 栈后进先出遵循后进先出规则最后存入的数据最先取出适合撤销操作、递归回溯、层级处理场景。StackintstacknewStackint();stack.Push(1);stack.Push(2);// 取出最后进入的数据intnumstack.Pop();七、新手高频易错坑点必避杜绝非泛型集合永远不用 ArrayList、Hashtable类型不安全、性能差避免 List 高频查询大数据量、频繁根据条件查询不要用 List 遍历匹配优先 DictionaryDictionary 键不可修改键是哈希寻址依据一旦添加无法修改仅能删除重建慎用 List 频繁扩容预知数据量建议初始化 Capacity减少数组复制开销遍历中禁止增删foreach 遍历集合时不能直接增删元素会触发迭代器异常可改用for循环或临时集合。八、全文核心总结数组长度固定、功能单一仅适合固定数量数据存储不适合动态业务老式非泛型集合ArrayList、Hashtable 存在装箱拆箱、类型不安全问题项目彻底淘汰List动态泛型数组适合批量存储、遍历、排序是最通用的数据容器底层基于数组自动2倍扩容Dictionary哈希键值对集合查询性能极致适合键值映射、高频查询场景队列、栈适配特定数据顺序场景分别遵循先进先出、后进先出规则集合选型核心遍历批量用List精准查询用Dictionary结合业务场景择优使用实现代码高性能、高规范。下期预告下一篇我们将精讲C# LINQ 核心语法彻底告别繁琐的循环遍历用极简语法实现集合筛选、排序、分组、联表查询大幅简化代码进阶极简高效编程