You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于字典实现特定时间段车辆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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:08:52