【问题标题】:C# Lambda Expression SpeedC# Lambda 表达式速度
【发布时间】:2010-03-10 09:05:18
【问题描述】:

我以前没有使用过很多 lambda 表达式,我遇到了一个我认为可以巧妙使用一个的情况。我有一个约 19,000 条记录的自定义列表,我需要找出列表中是否存在记录,因此我决定尝试以下操作,而不是编写一堆循环或使用 linq 来遍历列表:

for (int i = MinX; i <= MaxX; ++i)
{
    tempY = MinY;

    while (tempY <= MaxY)
    {
        bool exists = myList.Exists(item => item.XCoord == i && item.YCoord == tempY);

        ++tempY;
    }
}

唯一的问题是它需要大约 9 - 11 秒来执行。我做错了什么,这只是我不应该使用这样的表达式的情况吗?

谢谢。

编辑:对不起。我应该详细说明。我正在使用 for 和 while 循环创建记录列表,并检查该记录是否存在于 myList 中。这是我能想到的唯一方法。我会重新评估它,看看我想出了什么。

【问题讨论】:

  • 您为什么希望这会很快?这是一个没有任何索引或排序的嵌套搜索。
  • 我认为没有必要投反对票。这是一个有趣的问题,虽然措辞不佳。
  • 为什么外循环是for循环,内循环是while循环?
  • 所以我可以遍历所有 x 坐标,然后遍历所有 y 坐标。例如,将每个 x 视为一列,将每个 y 视为一行,我试图遍历该列中的每个单元格。

标签: c# lambda


【解决方案1】:

这段代码对我来说毫无意义,所以很难说你是否做错了。很有可能,是的,你做错了。

与其显示代码,不如试试这个。假设您有一个方法完全完成了您想要的。该方法的签名是什么?不是身体,只是签名。

例如,假设您想问“这个点列表是否包含特定点?”那么签名就是

bool Contains(List<Point> list, Point p)

假设您想问“这个点列表是否包含这个矩形内的任何点?”那么签名就是

bool ContainsAny(List<Point> list, Rectangle r)

假设您想问“这两个列表的共同点是什么?”那么签名将是

List<Point> Intersection(List<Point> l1, List<Point> l2)

等等。更清楚地说明您要做什么,我们可以帮助您优化该操作。从签名开始。

【讨论】:

  • +1 因为看起来 Nathan 确实在手动创建 Point,而他实际上可以只存储一个通用的点列表并使用 ContainsAny
【解决方案2】:

这不是 lambda 的问题,而是您的算法的问题。你从 MinX 循环到 MaxX 是多少?然后你从 MinY 循环到 MaxY,然后你循环通过 ~19,000 条记录。因此,如果 X 循环为 10,y 循环为 10,那么您将进行 19,000*10*10 次调用。情况可能会更糟。

【讨论】:

    【解决方案3】:

    您的算法效率低下。如果您在同一个列表上进行多次搜索,您需要:

    1. Sort 适当的列表(通过您的查找键)。
    2. 使用binary search 查找正确的记录。

    您的另一个选择是如果内存不是问题并且您希望它非常快,则将记录放入Dictionary&lt;Your_Key,Record&gt;。这将在您设置好之后为您提供最快的访问权限。

    【讨论】:

    • 这非常有效。只需 1 秒即可完成我想做的事情。
    【解决方案4】:

    这不是你想要的吗?

    myList.Where(
        item=>item.XCoord>=MinX
            &&item.XCoord<=MaxX
            &&item.YCoord>=MinY
            &&item.YCoord<=MaxY
    )
    

    所有生成的项目都将满足exists 标准。

    ...还是我误会了?

    【讨论】:

      【解决方案5】:

      我将用一个不错的基于 linq 的解决方案来扩展 Kevin 的答案。

      原始代码有效地计算了一个二维布尔数组,该数组指示myList 中每个数组元素的xy 坐标是否存在坐标。

      用于每个xy 的测试可以表示为如下的 lambda 函数:

      Func<int, int, bool> original =
          (x, y) =>
              myList.Exists(item => item.XCoord == x && item.YCoord == y);
      

      这是低效的,因为调用了Exists 方法,因此对每个xy 坐标测试的列表进行了迭代。是否迭代整个列表取决于是否找到匹配项。在很多情况下不会有匹配项,因此会多次访问整个列表。

      因此,最好预先计算一个字典字典以确定myList 中是否存在任何xy 坐标的坐标。

      var xyLookup =
          (from item in myList
           group item by item.XCoord into XCoords
           select new
           {
               X = XCoords.Key,
               YLookup = (from x in XCoords
                          group x by x.YCoord into YCoords
                          select new
                          {
                              Y = YCoords.Key,
                              Coords = YCoords
                          }).ToDictionary(a => a.Y, a => a.Coords)
           }).ToDictionary(b => b.X, b => b.YLookup);
      

      xyLookup 现在允许以下 lambda 函数替换原始版本。

      Func<int, int, bool> refactored =
          (x, y) =>
              xyLookup.ContainsKey(x) && xyLookup[x].ContainsKey(y);
      

      预计算 xyLookup 需要一些时间,因此根据我的测试,如果我有一个 3x3 数组并且 myList 包含 3 个坐标,那么这两种方法的速度大致相同。不过,我希望 myList 中的实际数组大小和元素数量在实践中会大得多。

      如果我在 myList 中有一个包含 10,000 个坐标的 100x100 数组,那么 xyLookup 比原始方法快大约 91 倍。

      我喜欢 linq... :-)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-17
        • 2017-10-02
        • 2015-12-23
        • 2011-01-26
        • 2020-07-02
        • 2010-12-22
        • 1970-01-01
        • 2011-06-19
        相关资源
        最近更新 更多