我需要过滤List<object>
,以便删除其他string
内部不存在List<string>
属性的所有项目。
我创建了这个控制台应用程序只是为了确保LINQ语法正确:
class FooBar
{
public int Id { get; set; }
public string ValueName { get; set; }
}
然后...
List<FooBar> foobars = new List<FooBar>
{
new FooBar { Id = 1, ValueName = "Val1" },
new FooBar { Id = 2, ValueName = "Val2" },
new FooBar { Id = 3, ValueName = "Val3" },
new FooBar { Id = 4, ValueName = "Val4" }
};
List<string> myStrings = new List<string>
{
"Val1",
"Val3"
};
// Only keep records where ValueName is found in `myStrings`
foobars = foobars.Where(f => myStrings.Contains(f.ValueName)).ToList();
因此,这一行:
foobars = foobars.Where(f => myStrings.Contains(f.ValueName)).ToList();
恰好满足了我的要求,并且还给了我这两个记录:
{ Id = 1, ValueName = "Val1" }
{ Id = 3, ValueName = "Val3" }
很好。但是...在实际应用中,foobars
有超过200k项,而myStrings
有约190k。而当该LINQ行被执行时,最多需要5分钟才能完成。
我显然做错了什么。 20万条记录并不大。真正的FooBar
并不是那么复杂(没有嵌套对象,只有9个属性)。
这是怎么回事?
答案 0 :(得分:6)
这里的问题是您正在做foobars.Where(f => myStrings.Contains(f.ValueName))
,因此对于foobars
中的每个项目,您正在检查myStrings
的所有个项目。
缩放为二次方。也称为O(n ^ 2),请阅读更多here。 因此,如果您有10 + 10个项目,则要进行100次检查(10 * 10),如果您有10,000 + 10,000个项目,则要进行100,000,000个检查。 就您而言,您要进行38,000,000,000次以上的检查;)
解决方案:从myStrings
创建一个HashSet,然后使用Contains
中的HashSet
。
例如替换为:
var myStringsSet = new HashSet<string>(myStrings);
foobars = foobars.Where(f => myStringsSet.Contains(f.ValueName)).ToList();
现在有10,000 + 10,000个项目,您将进行10,000张支票,而不是100,000,000张。对于您而言,这将是200,000张支票,而不是38,000,000,000张。