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

mns*_*nsc 2 c# linq optimization performance linq-to-objects

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

此代码更好地解释了问题,并产生了我想要的结果.但是对于较大的列表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<PropValue> propList;
        static List<Adjustment> adjList;

        public static void Main()
        {
            propList = new List<PropValue>{
                new PropValue{Name = "Alfa", Val=2.1M},
                new PropValue{Name = "Beta", Val=1.0M},
                new PropValue{Name = "Gamma", Val=8.0M}
            };
            adjList = new List<Adjustment>{
                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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

期望的结果是: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 };
Run Code Online (Sandbox Code Playgroud)

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

Jon*_*eet 6

该连接应该相当快,因为​​它将首先遍历所有adjList以创建查找,然后对于其中的每个元素propList将仅使用查找.这比较大的代码中的O(N*M)方法快 - 尽管可以通过在循环之前调用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;
    }
}
Run Code Online (Sandbox Code Playgroud)