基于字典实现特定时间段车辆MPG计算的方案合理性咨询
MPG跟踪程序:现有方案分析与优化建议
首先得说,你用人名作为键、车辆记录列表作为值的Dictionary来存储数据的思路是没问题的——毕竟核心需求是按人名查特定时间段的MPG,按人名分组能快速定位到目标用户的所有记录,不用全局遍历,方向是对的。但现有代码里藏着不少细节问题,而且从扩展性和健壮性来看,离“最佳方法”还有点距离,咱们一步步拆解:
先说说现有代码里的明显bug
这些问题不修复,程序根本没法正常工作:
- 解析索引错误:你写的
int.TryParse(info[2], out gallonsFilled),但CSV里加油量是第4个字段(索引为3),这会把里程数当成加油量解析,完全乱套。 - Car对象赋值错误:
CarName = personName明显不对,应该把carName赋值给CarName,不然所有车辆的型号都会变成人名,完全不符合需求。 - 字典操作冗余且易出错:你每次拿到
carList后,加完记录又把log[personName] = carList赋值回去,其实TryGetValue拿到的是字典里的列表引用,直接修改列表就会同步到字典里,不用再赋值。而且循环外声明的carList会导致新用户的记录被错误追加到之前的列表里(比如第一个用户的列表会被第二个用户复用)。 - 缺少数据校验:CSV里可能有空行、字段数量不对、解析失败的情况,直接存脏数据会影响后续计算。
再聊聊设计上的可改进点
- 数据结构扩展性不足:现在用
List<Car>存每个用户的记录,但如果用户有多辆车,每次计算MPG都要先过滤再分组,效率不高。如果换成双层字典(外层键是人名,内层键是车辆型号),就能直接拿到对应车辆的所有记录,查询更快。 - 单一职责未遵守:
LoadData既负责解析CSV又负责构建存储结构,以后如果要换数据源(比如从数据库读取),就得修改这个方法,不符合单一职责原则。
优化后的实现参考
第一步:修复现有代码的bug,规范编码
先解决基础问题,同时加上数据校验:
class Car { public string CarName { get; set; } public double MilesDriven { get; set; } public int GallonsFilled { get; set; } // 修正属性名拼写,符合C#命名规范 public DateTime DateFilled { get; set; } } class MPGCalc { // 用私有字段+下划线命名规范,避免外部直接修改 private Dictionary<string, List<Car>> _userLogs = new Dictionary<string, List<Car>>(); public void LoadData(string input) { if (string.IsNullOrWhiteSpace(input)) return; // 移除空行,避免解析无效内容 string[] lines = input.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { string[] info = line.Split(','); // 先校验字段数量,不符合的直接跳过 if (info.Length != 5) continue; // 逐个解析并校验,解析失败的行直接跳过 if (!double.TryParse(info[2], out double milesDriven)) continue; if (!int.TryParse(info[3], out int gallonsFilled)) // 修正索引为3 continue; if (!DateTime.TryParse(info[4], out DateTime fillupDate)) continue; string personName = info[0]; string carName = info[1]; // 简化字典操作:没有就新建列表,直接添加记录 if (!_userLogs.TryGetValue(personName, out List<Car> carList)) { carList = new List<Car>(); _userLogs[personName] = carList; } // 修正CarName的赋值 carList.Add(new Car { CarName = carName, MilesDriven = milesDriven, GallonsFilled = gallonsFilled, DateFilled = fillupDate }); } } }
第二步:实现GetRangeMPG方法
基于修复后的存储结构,按车辆分组计算MPG,同时处理除以0的边界情况:
public List<(string CarName, double MPG)> GetRangeMPG(string personName, DateTime startDate, DateTime endDate) { // 如果找不到用户,返回空列表 if (!_userLogs.TryGetValue(personName, out List<Car> userRecords)) return new List<(string, double)>(); // 过滤时间段内的记录,按车辆分组计算总里程和总加油量 var mpgResults = userRecords .Where(record => record.DateFilled >= startDate && record.DateFilled <= endDate) .GroupBy(record => record.CarName) .Select(group => { double totalMiles = group.Sum(r => r.MilesDriven); int totalGallons = group.Sum(r => r.GallonsFilled); // 避免除以0,MPG保留两位小数更直观 double mpg = totalGallons == 0 ? 0 : Math.Round(totalMiles / totalGallons, 2); return (group.Key, mpg); }) .ToList(); return mpgResults; }
第三步:可选优化——用双层字典提升查询效率
如果用户的车辆多、查询频繁,提前按车辆分组存储会进一步提升效率:
// 双层字典:键1=人名,键2=车辆型号,值=该用户该车辆的所有加油记录 private Dictionary<string, Dictionary<string, List<Car>>> _userCarLogs = new Dictionary<string, Dictionary<string, List<Car>>>(); // 对应的LoadData修改 public void LoadData(string input) { // ... 前面的解析和校验逻辑不变 ... if (!_userCarLogs.TryGetValue(personName, out Dictionary<string, List<Car>> carDict)) { carDict = new Dictionary<string, List<Car>>(); _userCarLogs[personName] = carDict; } if (!carDict.TryGetValue(carName, out List<Car> carList)) { carList = new List<Car>(); carDict[carName] = carList; } carList.Add(new Car { /* 赋值逻辑不变 */ }); } // GetRangeMPG可以更高效,无需再分组 public List<(string CarName, double MPG)> GetRangeMPG(string personName, DateTime startDate, DateTime endDate) { if (!_userCarLogs.TryGetValue(personName, out var carDict)) return new List<(string, double)>(); var result = new List<(string, double)>(); foreach (var (carName, records) in carDict) { var filteredRecords = records.Where(r => r.DateFilled >= startDate && r.DateFilled <= endDate); double totalMiles = filteredRecords.Sum(r => r.MilesDriven); int totalGallons = filteredRecords.Sum(r => r.GallonsFilled); double mpg = totalGallons == 0 ? 0 : Math.Round(totalMiles / totalGallons, 2); result.Add((carName, mpg)); } return result; }
总结
你的核心思路是合理的,但现有代码需要先修复bug。修正后,现有方案能满足基本需求;如果追求更高的效率和扩展性,双层字典的结构会更优。另外,添加数据校验、遵循编码规范能让代码更健壮、更容易维护。
内容的提问来源于stack exchange,提问作者RaksMen
相关产品推荐
相关产品推荐

