【问题标题】:Linq in large lists大型列表中的 Linq
【发布时间】:2014-02-27 15:16:31
【问题描述】:

我有两个自定义类GridElement

public class Grid
{
    public double ID { get; set; }

    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }

    public MyClass MoreProperties {get; set;}

    public Grid(int id, double x, double y, double z)
    {
        this.ID = id;
        this.X = x;
        this.Y = y;
        this.Z = z;
    }
}

Element:

public abstract class Element
{
    public  int ID { get; set; }

    public int NumberOfGrids { get; set; }

    public List<Grid> Grids { get; set; } //4 Grids in this case

    public Element()
    {
        Grids = new List<Grid>();              
    }
}

为了说明情况,请看这张图:

该类有一个名为Data的容器:

class Data : ModelBase
{
    public List<Grid> Grids{ get; set; }

    public List<Element> Elements { get; set; }
}

我阅读了包含大量数据的文本文件:网格和元素 这是网格的格式(简化):

网格 ID X Y Z

对于元素

元素 ID GRID1 GRID2 GRID3 GRID4

因此,GRID 条目提供了网格点的位置和 ID,ELEMENT 提供了该元素的网格 ID 及其自己的 ID。

我想要为每个元素关联所有 4 个网格,这样我将获得元素对象的每个网格inside 的坐标。

为此,我将文件读取了两次(因为元素条目位于网格之前并且为了简化事情):第一次读取它时,我填充了 Grids 列表(来自 Data 类)。 我第二次填写Elements 列表并做更多的事情。当我填写Elements 列表时,我只能填写关联的Grid 的ID。

如果您已经阅读到这里,我们有这个类 Data 包含两个列表 GridElements

对于关联我想出了这个方法:

public void AsociateGridsToElements()
{
    foreach (Element elem in Elements)
    {
        for (int i = 0; i < elem.Grids.Count; i++)
        {
            elem.Grids[i] = Grids.Where(g => g.ID == elem.Grids[i].ID).FirstOrDefault();
        }
    }
}

它遍历每个元素,然后遍历该元素的每个网格(在本例中为 4 个),然后在整个网格列表中查找具有相同 ID 的网格。当它找到第一个时,它分配该网格,这样元素就具有“完整”Grid 对象,而不是仅填充 ID 的对象(因为这是我读取文件时唯一能得到的东西)。

问题来了:这些文件非常大:大约 20 000 个网格点和 10 000 个元素,如果我循环每个元素,每次查看整个网格集合(4 次)它是:20 000 x 10 000 = 200 000 000 次操作。所以电脑处理不了,我觉得一定要改进。

谁能给个提示或帮助我优化这个问题?谢谢。

【问题讨论】:

    标签: c# wpf linq


    【解决方案1】:

    如果保证每个Grid 对象的ID 是唯一的,我将首先创建一个Grid 对象的字典,并将ID 作为字典中的键。那么在枚举 Elements 期间查找已填充的Grid 将只需要字典查找,而不是 Grids 列表的新枚举。

    public void AsociateGridsToElements()
    {
        var gridLookup = Grids.ToDictionary(grid => grid.ID);
    
        foreach (Element elem in Elements)
        {
            for (int i = 0; i < elem.Grids.Count; i++)
            {
                Grid fullyPopulatedGrid;
                if (gridLookup.TryGetValue(elem.Grids[i].ID, out fullyPopulatedGrid))
                {
                    elem.Grids[i] = fullyPopulatedGrid;
                }
                else
                {
                    // Unable to locate Grid Element
                }
            }
        }
    }
    

    在这种情况下,创建字典查找可以显着提高性能,因为它可以防止对 Grids 列表进行额外的枚举。

    上面的代码执行以下操作(根据您的估计):

    1. 枚举所有Grid 项目并为每个项目创建一个键值对。 (约 20,000 步)
    2. 枚举所有Element 项(约10,000 步)
    3. 枚举Grid 中的每个部分Element(称之为4 步)
    4. 对字典执行查找以查找正确填充的Grid(1 哈希查找)

    这里的总步数约为 20,000 + (10,000 * 4) * 2(每个元素/网格 1 个哈希查找)= 100,000 步

    您的原始代码执行以下操作:

    1. 枚举所有Element 项(约10,000 步)
    2. 枚举Grid 中的每个部分Element(称之为4 步)
    3. 枚举所有填充的 Grid 项目(大约 20,000 步)以找到第一个匹配项(这需要对每个元素/网格组合进行单独的迭代)

    这里的总步数约为 10,000 * 4 * 20,000 = 800,000,000 步

    【讨论】:

    • 这似乎工作得非常好,但我需要更多的测试。您能否解释一下这里发生了什么,以及为什么性能提高了这么多?
    • 我用一些关于操作数量的估计更新了我的答案。
    猜你喜欢
    • 2015-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多