Linq从其他列表中选择匹配列表

时间:2015-08-17 06:52:56

标签: c# performance linq

我有这样的课程

public class Category 
{
   CategoryID
   List<Product> ProductList
}
public class Product 
{
   CategoryID
}

List<Category>大约15k行,List <Product>大约40k行。我正在使用像这样的LINQ

CategoryList.ForEach(i =>
{
    i.ProductList = ProductList.Where(x => x.CategoryID == i.CategoryID).ToList();
});

我想知道有任何更好的性能。在其他情况下,数据可能会增加或减少

7 个答案:

答案 0 :(得分:12)

我认为使用并行性是愚蠢的,当对算法进行简单的更改可以将其加速千倍。假设CategoryID具有良好的GetHashCode()实现,与通过列表扫描的O(1)时间相比,字典/查找的查找时间几乎O(n)

两种可能的方法:

将类别转换为字典

假设类别具有唯一ID,您可以使用:

var categoryById = categories.ToDictionary(c => c.CategoryID);
foreach(var product in products)
{
    var category = categoryById[product.CategoryID];
    category.Products.Add(product);
}

这有运行时O(products.Count + categories.Count)并使用O(categories.Count)内存。

如果类别不是以空列表开头,则可能需要创建它。

将产品转换为Lookup

var productsByCategory = products.ToLookup(product => product.CategoryID);
foreach(var category in categories)
{
    category.Products = products[category.CategoryID].ToList();
}

这有运行时O(products.Count + categories.Count)并使用O(products.Count)内存。

由于产品通常多于类别,因此这种方法需要更多内存。另一方面,查找可能无需在类别对象中嵌入产品列表。

答案 1 :(得分:5)

您可以使用LINQ的并行实现 - PLINQ。通常PLINQ会提高LINQ的速度,因为它可以更有效地使用所有可用内核。

CategoryList.AsParallel().ForAll(i =>
     {
         i.ProductList = ProductList.AsParallel.Where(x => x.CategoryID == i.CategoryID).ToList();
     });

答案 2 :(得分:4)

您还可以使用GroupJoin代替多个wherehttps://msdn.microsoft.com/en-us/library/bb397905.aspx

虽然Parallel会为您提供最高nb核心增强功能,但您可以通过GroupJoin获得线性复杂性。

来自@CodesInChaos

  

如果类别和产品的数量相同,您将获得从二次到线性运行时的加速

答案 3 :(得分:3)

您可以在组织按类别(例如Dictionary<int, List<Product>>)分组的产品列表时获得效果。然后,您只需按键选择类别的产品列表,而不是在整个产品列表上执行LINQ查询并生成新的子列表。

CategoryList.ForEach(i =>
{
    i.ProductList = ProductDict[i.CategoryID];
});

答案 4 :(得分:2)

在测试性能之后,我发现比之前发布的更好的解决方案是:

    static List<Category> FilterList(List<Category> list, List<Product> ProductList)
    {
        Parallel.For(0, list.Count, i =>
        {
            list[i].ProductList = new List<Product>();
            for (int i2 = 0; i2 < ProductList.Count; i2++)
                if (ProductList[i2].CategoryID == list[i].CategoryID) list[i].ProductList.Add(ProductList[i2]);
        });            

        return list;
    }

然后list = FilterList(list, products);

对于3,500,000次操作,只需200ms

答案 5 :(得分:2)

实际上,在这种情况下最有效的解决方案是从ProductList创建CategoryList。像这样:

CategoryList = ProductList
  .GroupBy(p=>p.CategoryID)
  .Select(
    g=>
      new Category{
        CategoryID=g.Key,
        ProductList = g.ToList()
      }
  );

答案 6 :(得分:2)

如果CategoryList已存在,您可以先按CategoryID对产品进行分组,然后将分组的产品加入类别。

var query =
    from p in ProductList
    group p by p.CategoryID
        into grouped        
    join c in CategoryList
        on grouped.Key equals c.CategoryID
    select new { c, grouped };

然后使用循环设置Category对象的ProductList字段。

foreach (var item in query)
    item.c.ProductList = item.grouped.ToList();