LINQ(到对象)查询是否可能包含无限循环

本文关键字:包含 无限循环 是否 查询 对象 LINQ | 更新日期: 2023-09-27 17:58:22

我有一个简单的类:

public class RawBomItem
{
    private string material;
    private string item;
    private string component;
    private string quantity;
    private string b;
    private string spt;
...
}

每个数据成员都有一个属性。

然后我有一个包含这个类实例的列表

    private List<RawBomItem> rawBom;

该清单包含7万多个项目。

此时,我想在此列表上运行一个稍微复杂的LINQ查询。

List<string> endProducts = new List<string>(
    rawBom.Where(x1 => new List<string>(rawBom.Select(x2 => x2.Component)
                                              .Distinct())
                           .Contains(x1.Material) && (x1.B != "F"))
          .Select(x3 => x3.Material));

查询似乎进入了一个无限循环。(我等了几分钟才把它关掉)

我会把它变成DB来工作,我只是对问题所在感兴趣。

LINQ(到对象)查询是否可能包含无限循环

我不知道怎么会有一个无限循环,但你的代码效率非常低
对于rawBom中的每个项目,您计算不同的组件集,并将它们复制到一个新列表中。因此,在您的列表中有70000个项目时,您将执行70000^2=4900000000次迭代。此外,对于列表中的每一项,您都在再次迭代不同组件的列表。根据您有多少不同的组件,您可以在顶部添加相同数量的迭代。

这是可以改进的:

var components = new HashSet<string>(rawBom.Select(x => x.Component).Distinct());
var endProducts = rawBom.Where(x => components.Contains(x.Material) &&
                                    x.B != "F")
                        .Select(x => x.Material)
                        .ToList();
  1. 我们从主查询中提取不同组件列表的创建,因此我们只需要计算一次,而不是70000次
  2. 我们使用HashSet<string>而不是List<string>。这将对Contains的调用从O(n)更改为O(1)

最终的结果是,您只枚举了两次列表,结果只有140000次迭代。现在将其与原始迭代次数进行比较。

它是一个无限循环,这有点遥不可及。除非你的序列是一个生成器,但这里没有。

你所拥有的是效率低下的代码。

rawBom.Where( // this goes over the 70,000 item collection once

对于你拥有的70000人中的每一个人:

new List<string>(rawBom.Select(x2 => x2.Component).Distinct()) // this goes over the 70,000 collection 3 times (first select, then distinct, then new list)
.Contains // another bunch of accesses depending on how many distinct items there were
.Select(x3 => x3.Material) // iterates over the resulting collection once

因此,毫无疑问,这将是缓慢的。

List<RawBomItem> list = new List<RawBomItem>();
var components = list.Select(x => x.component).Distinct();
var b = new Func<RawBomItem, bool>(x =>
  {
    return components.Contains(x.Material) && x.B != "F";
  });
var v = list.Where(x => b(x)).Select(x1 => x1.material).ToList();

我会稍微简化您的查询。我想这就是你想要的?

List<string> endProducts = rawBom
.Where(x1 => rawBom.Any(x2 => x2.Component == x1.Material) && x1.B != "2")
.Select(x1 => x1.Material).ToList();

这不会为rawBom中的每个项目创建任何额外的列表,但会使用它本身