LINQ性能差异及通用Populate方法动态高效实现优化问询
问题2:优化Populate通用方法的性能
你的Populate方法慢的核心原因,就是每次遍历左集合时,都要调用FirstOrDefault把整个右集合扫一遍——13000条TA×4000条TB,就是5200万次遍历,耗时肯定超10秒。而LINQ Join快,就是因为它提前把右集合转成了哈希表,查找效率拉满。
那我们直接把这个逻辑搬到Populate里就行,优化后的代码如下:
public static void Populate<TL, TR>(IEnumerable<TL> lList, IEnumerable<TR> rList) { // 假设你已经通过反射获取到以下三个PropertyInfo: // propLeftKey: TL中用于关联的键属性(比如TA.BId) // propRightKey: TR中的主键属性(比如TB.Id) // propForeignObj: TL中要赋值的外对象属性(比如TA.B) // 第一步:把右集合转成字典,键是主键值,值是TR对象——这一步是性能提升的核心 var rightDict = rList.ToDictionary(r => propRightKey.GetValue(r)); // 第二步:遍历左集合,直接从字典里取匹配项赋值,O(1)查找 foreach (var l in lList) { var leftKeyValue = propLeftKey.GetValue(l); if (rightDict.TryGetValue(leftKeyValue, out var matchedR)) { propForeignObj.SetValue(l, matchedR); } else { // 没有匹配项时的处理,比如设为null,根据你的业务需求调整 propForeignObj.SetValue(l, default(TR)); } } }
这样修改后,时间复杂度直接从O(n*m)降到O(n+m),性能和你原来的LINQ Join写法基本持平,处理13000+4000的数据应该能轻松压到1秒以内。
额外的进阶优化建议:
- 如果右集合里有重复的主键值,
ToDictionary会抛出异常,这时候可以用GroupBy分组后取第一个,或者根据业务逻辑处理重复项; - 反射的
GetValue/SetValue有一定性能开销,如果你的场景对性能要求极高,可以提前用表达式树编译委托来替代反射:
// 提前编译获取左键值的委托,避免每次循环都反射 Func<TL, object> getLeftKey = Expression.Lambda<Func<TL, object>>( Expression.Convert(Expression.Property(Expression.Parameter(typeof(TL)), propLeftKey), typeof(object)), Expression.Parameter(typeof(TL)) ).Compile(); // 同理编译获取右键值、设置外对象的委托,循环里直接调用委托就行
这能进一步减少反射带来的性能损耗。
内容的提问来源于stack exchange,提问作者Tony
相关产品推荐
相关产品推荐

