C#按年、员工代码累加月度TotalOt值(优化性能)
优化加班累计数据的计算性能方案
现有代码
namespace Datas { public class OtOverviewData { public int Month { get; set; } public int Year { get; set; } public List<OtOverviewItem> ListItems { get; set; } = new List<OtOverviewItem>(); } public class OtOverviewItem { public string EmployeeCode { get; set; } public double TotalOt { get; set; } = 0; } } List<OtOverviewData> otData = new List<OtOverviewData>(); // 手动填充数据 var otAccumulateData = otData; // 注意:此处仅为引用赋值,并非真正克隆数据
需求说明
需要对otAccumulateData中的TotalOt字段,按相同年份(Year)、相同员工编码(EmployeeCode),以月度递增规则累加:
- 2月累计值 = 1月数据值 + 2月原始数据值
- 3月累计值 = 1月数据值 + 2月数据值 + 3月原始数据值
此前采用嵌套for循环实现,性能不佳,寻求更高效的实现方式。
优化实现方案
核心思路
- 修正数据克隆问题:原代码的引用赋值会导致修改累计数据时同步改动原集合,需创建深拷贝的新集合避免副作用。
- 分组+排序+单次累加:先按
Year和EmployeeCode分组,每组内按Month升序排序,再通过一次遍历完成累计计算,避免嵌套循环的重复遍历开销。
代码实现
// 1. 深拷贝原数据,隔离原集合与累计集合 var otAccumulateData = otData .Select(od => new OtOverviewData { Year = od.Year, Month = od.Month, ListItems = od.ListItems.Select(item => new OtOverviewItem { EmployeeCode = item.EmployeeCode, TotalOt = item.TotalOt }).ToList() }) .ToList(); // 2. 按年份+员工编码分组,整合所有月度数据 var employeeMonthlyGroups = otAccumulateData .SelectMany(od => od.ListItems.Select(item => new { od.Year, od.Month, item.EmployeeCode, item.TotalOt })) .GroupBy(x => new { x.Year, x.EmployeeCode }); // 3. 每组按月份升序遍历,计算累计值并更新回集合 foreach (var group in employeeMonthlyGroups) { double accumulatedOt = 0; foreach (var monthlyData in group.OrderBy(x => x.Month)) { accumulatedOt += monthlyData.TotalOt; // 定位到对应数据项并更新累计值 var targetItem = otAccumulateData .First(od => od.Year == group.Key.Year && od.Month == monthlyData.Month) .ListItems .First(item => item.EmployeeCode == group.Key.EmployeeCode); targetItem.TotalOt = accumulatedOt; } }
性能优势
- 利用Linq的
GroupBy哈希表分组逻辑,分组操作时间复杂度接近O(n),远优于嵌套循环的O(n²)。 - 每组仅需一次排序+一次遍历即可完成累加,避免了重复遍历整个集合的冗余操作。
- 逻辑清晰,后续维护成本更低。
内容的提问来源于stack exchange,提问作者khanh9x6
相关产品推荐
相关产品推荐

