如何优化C#中对比Excel两列的嵌套For循环以缩短执行时间
优化Excel数据对比代码的几种方法
你的代码慢主要有两个核心问题:一是嵌套循环带来的O(n*m)时间复杂度,数据量上来后计算量直接爆炸;二是频繁读写Excel单元格,每次和Excel的COM交互都属于跨进程操作,效率极低。下面是具体的优化方案:
1. 一次性把数据读入内存数组,减少Excel交互
不要每次循环都去访问ws5.Cells[i,3].Value,这种反复跨进程调用是拖慢速度的关键。先把两个工作表的UsedRange数据一次性读取到内存二维数组里,后续所有对比逻辑都在内存中完成:
// 读取ws5全部数据到内存数组 object[,] ws5Data = ws5.UsedRange.Value; int ws5RowCount = ws5.UsedRange.Rows.Count; // 读取ws6全部数据到内存数组 object[,] ws6Data = ws6.UsedRange.Value; int ws6RowCount = ws6.UsedRange.Rows.Count;
2. 用哈希表建立索引,替换嵌套循环
把ws6的第2列(匹配键)和对应的第3列数值、行号存入Dictionary,这样遍历ws5时直接通过键查找匹配项,把嵌套循环的O(n*m)时间复杂度降到O(n+m):
// 键:第2列的字符串值,值:存储第3列数值+对应行号的列表 Dictionary<string, List<(double Col3Value, int RowNum)>> ws6Index = new Dictionary<string, List<(double, int)>>(); // 遍历ws6数据构建索引(注意Excel行从2开始,对应数组索引是1,因为数组从0起始) for (int n = 2; n <= ws6RowCount; n++) { string col2Str = Convert.ToString(ws6Data[n, 2]); double col3Val = Convert.ToDouble(ws6Data[n, 3]); if (!ws6Index.ContainsKey(col2Str)) { ws6Index[col2Str] = new List<(double, int)>(); } ws6Index[col2Str].Add((col3Val, n)); }
3. 批量更新Excel,避免逐个单元格操作
先在内存中记录需要修改的单元格信息(颜色、数值),最后一次性写入Excel,大幅减少和Excel的交互次数。同时关闭Excel的实时刷新和事件触发,进一步提升速度:
// 关闭Excel屏幕更新和事件触发,临时禁用界面刷新 ws5.Application.ScreenUpdating = false; ws5.Application.EnableEvents = false; // 遍历ws5数据做对比 for (int i = 2; i <= ws5RowCount; i++) { string ws5Col2Str = Convert.ToString(ws5Data[i, 2]); double ws5Col3Val = Convert.ToDouble(ws5Data[i, 3]); if (ws6Index.TryGetValue(ws5Col2Str, out var matches)) { foreach (var match in matches) { if (match.Col3Value == ws5Col3Val) { // 标记橙色 ws5.Cells[i, 2].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Orange); ws5.Cells[i, 3].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Orange); ws6.Cells[match.RowNum, 2].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Orange); ws6.Cells[match.RowNum, 3].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Orange); } else { // 计算差值并写入 double diff = match.Col3Value - ws5Col3Val; ws5.Cells[i, 4].Value = diff; ws6.Cells[match.RowNum, 4].Value = -diff; // 标记黄色 ws5.Cells[i, 2].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Yellow); ws5.Cells[i, 3].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Yellow); ws6.Cells[match.RowNum, 2].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Yellow); ws6.Cells[match.RowNum, 3].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Yellow); } } } // 进度条只在外层循环更新,避免频繁刷新 progressBar1.Value = (100 * (i - 1)) / (ws5RowCount - 1); } // 恢复Excel的屏幕更新和事件触发 ws5.Application.ScreenUpdating = true; ws5.Application.EnableEvents = true;
额外优化细节
- 提前转换数据:把需要用到的列数据提前转换为字符串/数值,避免循环中重复执行转换操作
- 唯一值优化:如果ws6的第2列是唯一值,Dictionary的值可以不用列表,直接存储单个元组,进一步提升查找效率
- 批量格式设置:如果需要标记的行很多,可以先收集行号范围,再一次性设置整行/整列的格式,比逐个单元格设置更快
内容的提问来源于stack exchange,提问作者Najm
相关产品推荐
相关产品推荐

