C#中如何优化列表遍历速度
一个数据库表(id,name,…)中有10万条记录,查找name=’guoguo‘可能需要很长时间,但是如果对name建立了索引,那么再用name=’guoguo‘来查询将变得非常快(有多快?自己可以去试试)。
相应的,有时候我们的代码里面会用到List<T>,Array来存储一组数据。我们以一个例子来说明一下。
定义数据类型:
public class UserInfo { public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } }
示例代码有个前提:name不会重复。从List中按照name找到对应的UserInfo可以用以下代码:
List<UserInfo> _userinfos; public UserInfo GetUserInfoByName(string name) { for (int i = 0; i < _userinfos.Count; i++) { UserInfo ui = _userinfos[i]; if (ui.Name == name) { return ui; } } return null; } public UserInfo GetUserInfoByName_Find(string name) { return _userinfos.Find((temp) => { return temp.Name == name; }); }
两个方法其实都是使用遍历列表的方式去比较、查找;我做了实验,如果列表中有10万条记录,分别执行“查找第5万个元素”1000遍的耗时为:
GetUserInfoByName |
1877毫秒 |
GetUserInfoByName_Find |
2290毫秒 |
也就是说平均一次要2毫秒左右(Find效率还更低);
OK,接下来我们要试着借鉴数据库的方式给List建立索引。
public List<UserInfo> Userinfos { get { return _userinfos; } set { _userinfos = value; MakeIndex(); } } Dictionary<string, UserInfo> _userIndex; /// <summary> /// 建立索引 /// </summary> private void MakeIndex() { Dictionary<string, UserInfo> userIndex = new Dictionary<string, UserInfo>(); foreach (var item in _userinfos) { userIndex.Add(item.Name, item); } _userIndex = userIndex; }
MakeIndex方法在Userinfos的set属性器里面调用;那么查询的方法就应该这么写:
public UserInfo GetUserInfoByNameEx(string name) { return _userIndex[name]; }
这么简单?对,就是这么简单,而且,效率特别高。与上面的两个方法一起进行比较:
建立索引+查找10万次=36毫秒!
原因就是Dictionary[Key]方法的时间复杂度为O(1),几乎不耗时。
注意:本示例为了代码简洁,对于key重复,null引用等等都没有判断,实际编码过程中请注意考虑这些情况。
以下是示例的所有代码:

using System; using System.Collections.Generic; using System.Diagnostics; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { List<UserInfo> userinfos = new List<UserInfo>(); //创建10万个数据 for (int i = 0; i < 10 * 10000; i++) { UserInfo ui = new UserInfo(); ui.ID = i; ui.Name = Guid.NewGuid().ToString(); userinfos.Add(ui); } UserUtil util = new UserUtil(); //用于计时 Stopwatch sw = new Stopwatch(); sw.Start(); util.Userinfos = userinfos; sw.Stop(); Console.WriteLine(string.Format("建立索引MakeIndex耗时:{0}毫秒", sw.ElapsedMilliseconds)); //以第5万个名字作为要查找的名字 string nameToFind = userinfos[5 * 10000].Name; sw.Reset(); sw.Start(); for (int i = 0; i < 1000; i++) { util.GetUserInfoByName(nameToFind); } sw.Stop(); Console.WriteLine(string.Format("执行1000次GetUserInfoByName耗时:{0}毫秒", sw.ElapsedMilliseconds)); sw.Reset(); sw.Start(); for (int i = 0; i < 1000; i++) { util.GetUserInfoByName_Find(nameToFind); } sw.Stop(); Console.WriteLine(string.Format("执行1000次GetUserInfoByName_Find耗时:{0}毫秒", sw.ElapsedMilliseconds)); sw.Reset(); sw.Start(); for (int i = 0; i < 10 * 10000; i++) { util.GetUserInfoByNameEx(nameToFind); } sw.Stop(); Console.WriteLine(string.Format("执行10万次GetUserInfoByNameEx耗时:{0}毫秒", sw.ElapsedMilliseconds)); Console.ReadKey(); } } public class UserUtil { List<UserInfo> _userinfos; public List<UserInfo> Userinfos { get { return _userinfos; } set { _userinfos = value; MakeIndex(); } } Dictionary<string, UserInfo> _userIndex; public UserInfo GetUserInfoByName(string name) { for (int i = 0; i < _userinfos.Count; i++) { UserInfo ui = _userinfos[i]; if (ui.Name == name) { return ui; } } return null; } public UserInfo GetUserInfoByName_Find(string name) { return _userinfos.Find((temp) => { return temp.Name == name; }); } /// <summary> /// 建立索引 /// </summary> private void MakeIndex() { Dictionary<string, UserInfo> userIndex = new Dictionary<string, UserInfo>(); foreach (var item in _userinfos) { userIndex.Add(item.Name, item); } _userIndex = userIndex; } public UserInfo GetUserInfoByNameEx(string name) { return _userIndex[name]; } } public class UserInfo { public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?