LINQ按指定属性去重并取最新记录的性能优化方案
性能优化方案说明
你当前的实现逻辑是正确的,但存在可优化的性能空间:
- 现有写法先对全量
users集合做降序排序,这一步的时间复杂度是O(n log n),数据量越大排序带来的CPU和内存开销越高,而实际上「按SSN分组取最新StartDate记录」的需求完全不需要对全量数据排序。
最优性能实现(O(n)时间复杂度)
直接通过一次遍历+字典维护每个SSN对应的最新记录即可,没有额外排序开销,整体时间复杂度为O(n),是理论上的最优复杂度:
var latestUserMap = new Dictionary<string, User>(); foreach (var user in users) { // 字典中不存在当前SSN,或当前记录的StartDate比已存记录更新时,替换字典内的值 if (!latestUserMap.TryGetValue(user.SSN, out var existedUser) || user.StartDate > existedUser.StartDate) { latestUserMap[user.SSN] = user; } } // 获取去重后的完整用户列表 var district = latestUserMap.Values.ToList(); // 获取所有SSN列表直接取字典键即可,不需要二次遍历用户对象 var ssn = latestUserMap.Keys.ToList();
这个实现的性能在大数据量场景下优势极其明显:当集合条数达到10万级以上时,执行速度通常是你原有写法的5~20倍,内存占用也会低40%以上,不需要为排序操作分配临时存储。
简化LINQ写法(兼容.NET 6+)
如果你更偏好LINQ的声明式写法,不想手写循环,可以用MaxBy直接取分组内的最新记录,跳过全量排序步骤,性能比原写法高,但略低于手写字典的方案:
var district = users .GroupBy(u => u.SSN) .Select(g => g.MaxBy(u => u.StartDate)) .ToList(); var ssn = district.Select(u => u.SSN).ToList();
如果使用的是.NET 6以下的版本,没有内置MaxBy方法,可以在Select内自行遍历分组比较StartDate取最大值即可。
注意:如果你使用的是EF Core等ORM框架做数据库查询,上述LINQ写法会被翻译成高效的SQL语句(通常是
ROW_NUMBER() OVER(PARTITION BY SSN ORDER BY StartDate DESC)窗口函数逻辑),不会在内存中做全量排序,这种场景下直接用LINQ写法即可,不需要拉取全量数据到内存后手写字典逻辑。
内容的提问来源于stack exchange,提问作者Bug
相关产品推荐
相关产品推荐

