使用HashSet实现厂商去重及数量累加的问题求助
我有一个格式为(Id、ProductId、Title、Quantity、Manufacturer)的items列表,具体数据如下:
| Id | ProductId | Title | Quantity | Manufacturer |
|---|---|---|---|---|
| 1 | 10 | 蓝光播放器 | 3 | Sony |
| 2 | 20 | CD播放器 | 2 | Sony |
| 3 | 30 | 冰箱 | 1 | LG |
| 4 | 40 | 洗衣机 | 1 | Samsung |
我希望按厂商分组返回厂商ID、厂商名称、总数量的汇总数据,预期结果如下:
| Manufacturer Id | Manufacturer Name | Quantity |
|---|---|---|
| 1234 | Sony | 5 |
| 1235 | LG | 1 |
| 1236 | Samsung | 1 |
我编写了如下GetManufacturerData方法:
public void GetManufacturerData() { var manData = new HashSet<ManufacturerAndQuantity>(); foreach (var item in items) { var mq = new ManufacturerAndQuantity(); var manufac = myService.GetManufacturer(item.ProductId); mq.Title = manufac.Title; mq.Id = manufac.Id; mq.Total += item.Quantity; if (!manData.Equals(c)) { manData.Add(c); } } }
对应的ManufacturerAndQuantity类实现了IEquatable<ManufacturerAndQuantity>接口:
public class ManufacturerAndQuantity : IEquatable<ManufacturerAndQuantity> { public int? Id { get; set; } public string Title { get; set; } public int Total{ get; set; } public bool Equals(ManufacturerAndQuantity? other) { return this.Id.Equals(other.Id); } }
原本认为HashSet会自动过滤重复项,但目前它仍会添加相同厂商的条目(如Sony被添加两次),且未正确累加总数量。请问我的代码哪里存在问题?
补充说明:只能通过ProductId调用外部服务获取厂商信息,两者无直接关联。
1. 变量引用错误
代码中出现未定义变量c,原本应该引用刚创建的mq对象,这会直接导致逻辑错误——你根本没把当前处理的厂商条目和HashSet做有效性对比。
2. HashSet存在性判断逻辑错误
你调用manData.Equals(c)是把整个HashSet对象和变量比较,而非检查HashSet中是否包含该厂商条目。正确做法是用manData.Contains(mq)判断是否已存在相同厂商的记录。
3. 未实现已有厂商的数量累加
即使HashSet能识别重复项,你也没有对已存在的厂商条目进行数量累加,而是每次都创建新对象,直接导致同厂商条目重复添加。
4. 未重写GetHashCode方法
实现IEquatable<T>接口时必须同时重写GetHashCode,HashSet依赖哈希码快速定位元素。只重写Equals不重写GetHashCode,会导致HashSet认为两个Id相同的对象是不同的(默认哈希码基于对象引用)。
修复后的代码
修正ManufacturerAndQuantity类
public class ManufacturerAndQuantity : IEquatable<ManufacturerAndQuantity> { public int? Id { get; set; } public string Title { get; set; } public int Total { get; set; } public bool Equals(ManufacturerAndQuantity? other) { if (other == null) return false; // 处理Id为null的情况,避免空引用异常 return Id.HasValue && other.Id.HasValue && Id.Value.Equals(other.Id.Value); } // 必须重写GetHashCode,基于判断相等的核心字段(Id) public override int GetHashCode() { return Id.HasValue ? Id.Value.GetHashCode() : 0; } // 重写Object.Equals保持逻辑一致性 public override bool Equals(object? obj) { return Equals(obj as ManufacturerAndQuantity); } }
修正GetManufacturerData方法
public void GetManufacturerData() { var manData = new HashSet<ManufacturerAndQuantity>(); foreach (var item in items) { var manufac = myService.GetManufacturer(item.ProductId); // 先检查HashSet中是否已有该厂商的条目 var existingMq = manData.FirstOrDefault(mq => mq.Id == manufac.Id); if (existingMq != null) { // 已有条目,累加数量 existingMq.Total += item.Quantity; } else { // 无对应条目,创建新对象并添加 var mq = new ManufacturerAndQuantity { Id = manufac.Id, Title = manufac.Title, Total = item.Quantity }; manData.Add(mq); } } }
额外优化建议
可以先对items按ProductId分组,减少外部服务调用次数:
// 按ProductId分组,避免重复调用GetManufacturer var groupedItems = items.GroupBy(i => i.ProductId); foreach (var group in groupedItems) { var manufac = myService.GetManufacturer(group.Key); var totalQuantity = group.Sum(i => i.Quantity); var existingMq = manData.FirstOrDefault(mq => mq.Id == manufac.Id); if (existingMq != null) { existingMq.Total += totalQuantity; } else { manData.Add(new ManufacturerAndQuantity { Id = manufac.Id, Title = manufac.Title, Total = totalQuantity }); } }
内容的提问来源于stack exchange,提问作者KeithViking

