使用linq更新对象的性能更佳

我有两个自定义对象列表,如果另一个列表中有一个与另一对字段匹配的对象,则希望更新一个列表中所有对象的字段。

此代码更好地解释了问题,并产生了我想要的结果。 但是对于较大的列表20k和具有匹配对象的20k列表,这需要相当长的时间(31秒)。 通过使用通用列表Find(Predicate)方法,我可以使用~50%来改善这一点。

using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; namespace ExperimentFW3 { public class PropValue { public string Name; public decimal Val; public decimal Total; } public class Adjustment { public string PropName; public decimal AdjVal; } class Program { static List propList; static List adjList; public static void Main() { propList = new List{ new PropValue{Name = "Alfa", Val=2.1M}, new PropValue{Name = "Beta", Val=1.0M}, new PropValue{Name = "Gamma", Val=8.0M} }; adjList = new List{ new Adjustment{PropName = "Alfa", AdjVal=-0.1M}, new Adjustment{PropName = "Beta", AdjVal=3M} }; foreach (var p in propList) { Adjustment a = adjList.SingleOrDefault( av => av.PropName.Equals(p.Name) ); if (a != null) p.Total = p.Val + a.AdjVal; else p.Total = p.Val; } } } } 

期望的结果是:Alfa total = 2,Beta total = 4,Gamma total = 8

但我想知道这是否有可能做得更快。 即使在结果集中循环超过20k项时,内部连接两个列表也只需要很少的时间。

 var joined = from p in propList join a in adjList on p.Name equals a.PropName select new { p.Name, p.Val, p.Total, a.AdjVal }; 

所以我的问题是,是否可以做一些像T-SQL那样的事情? 来自左连接的UPDATE使用ISNULL(val,0)对调整值进行更新。

该连接应该相当快,因为​​它将首先遍历所有adjList以创建查找,然后对于propList每个元素,它将仅使用查找。 这比较大的代码中的O(N * M)方法要快 – 尽管可以通过在循环之前在adjList上调用ToLookup (或ToDictionary因为您只需要一个值)来adjList

编辑:这是使用ToDictionary的修改代码。 未经测试,请注意……

 var adjDictionary = adjList.ToDictionary(av => av.PropName); foreach (var p in propList) { Adjustment a; if (adjDictionary.TryGetValue(p.Name, out a)) { p.Total = p.Val + a.AdjVal; } else { p.Total = p.Val; } } 

如果adjList可能具有重复的名称,则应在推送到字典之前对项目进行分组。

 Dictionary adjDictionary = adjList .GroupBy(a => a.PropName) .ToDictionary(g => g.Key, g => g.Sum(a => a.AdjVal)) propList.ForEach(p => { decimal a; adjDictionary.TryGetValue(p.Name, out a); p.Total = p.Val + a; }); 

我知道我迟到了这个,但我想有人会欣赏下面更清晰的简短答案,它在adjList中处理每个查询的多个记录。 创建LookUp将允许快速查找多个项目,如果LookUp中没有记录,则返回空列表。

 var adjLookUp = adjList.ToLookUp(a => a.PropName); foreach (var p in propList) p.Total = p.Val + adjLookUp[p.Name].Sum(a => a.AdjVal);