优化LINQ to Objects查询

时间:2012-09-11 16:46:53

标签: c# linq-to-objects


我在列表中有大约200K的记录,我正在循环它们并形成另一个集合。这在我的本地64位Win 7上工作正常但是当我将它移动到Windows Server 2008 R2时,需要花费很多时间。几乎差不多一个小时!

我试着查看编译查询,但我仍在计算它 由于各种原因,我们无法进行数据库连接并检索子值

以下是代码:

//listOfDetails is another collection
List<SomeDetails> myDetails = null;
foreach (CustomerDetails myItem in customerDetails)
{

    var myList = from ss in listOfDetails
                 where ss.CustomerNumber == myItem.CustomerNum
                 && ss.ID == myItem.ID
                 select ss;
     myDetails = (List<SomeDetails>)(myList.ToList());
     myItem.SomeDetails = myDetails;
}

4 个答案:

答案 0 :(得分:6)

我会这样做:

var lookup = listOfDetails.ToLookup(x => new { x.CustomerNumber, x.ID });
foreach(var item in customerDetails)
{
    var key = new { CustomerNumber = item.CustomerNum, item.ID };
    item.SomeDetails = lookup[key].ToList();
}

这段代码的最大好处是它只需要遍历listOfDetails一次即可构建查找 - 这只不过是一个哈希映射。之后我们只使用密钥获取值,这非常快,因为这是为哈希映射构建的。

答案 1 :(得分:3)

我不知道为什么你的性能有所不同,但你应该能够让代码表现得更好。

//listOfDetails is another collection
List<SomeDetails> myDetails = ...;
detailsGrouped = myDetails.ToLookup(x => new { x.CustomerNumber, x.ID });
foreach (CustomerDetails myItem in customerDetails)
{ 
    var myList = detailsGrouped[new { CustomerNumber = myItem.CustomerNum, myItem.ID }];
    myItem.SomeDetails = myList.ToList();
}

这里的想法是避免在myDetails上重复循环,而是构建基于散列的查找。一旦构建完成,进行查找非常便宜。

答案 2 :(得分:1)

内部ToList()正在强制对每个循环进行评估,这会受到伤害。 SelectMany可能会让你避免ToList,如下所示:

var details = customerDetails.Select( item => listOfDetails
    .Where( detail => detail.CustomerNumber == item.CustomerNum)
    .Where( detail => detail.ID == item.ID)
    .SelectMany( i => i as SomeDetails )
);

如果您首先获取所有SomeDetails然后将它们分配给项目,则可能会加快速度。或者它可能不会。你应该真正剖析一下,看看时间在哪里。

答案 3 :(得分:1)

我想你可能会从这里的加入中受益,所以:

var mods = customerDetails
    .Join(
        listOfDetails, 
        x => Tuple.Create(x.ID, x.CustomerNum), 
        x => Tuple.Create(x.ID, x.CustomerNumber),
        (a, b) => new {custDet = a, listDet = b})
    .GroupBy(x => x.custDet)
    .Select(g => new{custDet = g.Key,items = g.Select(x => x.listDet).ToList()});

foreach(var mod in mods)
{
    mod.custDet.SomeDetails = mod.items;
}

我没有编译这段代码......

通过连接,可以通过在O(n)时间内构建第二个列表的类似哈希表的集合(Lookup)来完成从一个列表到另一个列表的项目的匹配。然后,这是迭代第一个列表并从Lookup中提取项目的问题。由于从哈希表中提取数据是O(1),迭代/匹配阶段也只需要O(n),后续的GroupBy也是如此。所以在所有的操作中应该取〜O(3n),相当于O(n),其中n是较长列表的长度。