LINQ 使用 Skip 进行数量判断提速
当我们需要判断集合是否有元素时,很容易想到:
list.Count() > 0
改成:
list.Any()
因为 Any() 只需要确认有没有第一个元素,找到之后就可以停止遍历。
但是,当需求变成“满足复杂条件的元素是否超过 5 个”时,很多人首先想到的还是:
list.Count(x => ComplexCondition(x)) > 5
这段代码当然没有问题,但它会把整个序列遍历完,计算出满足条件的元素总数。实际上,我们只想知道有没有第 6 个元素,并不关心最终到底是 6 个、60 个,还是 6000 个。
这时可以使用 Where、Skip 和 Any:
bool result = list
.Where(x => ComplexCondition(x))
.Skip(5)
.Any();
这段代码的意思是:先筛选满足条件的元素,跳过前 5 个,再判断后面是否还有元素。如果跳过 5 个以后仍然有元素,就说明至少存在第 6 个匹配项,也就是数量大于 5。
为什么可以提速
Count(predicate) 需要遍历完整个序列,才能得到准确数量:
list.Count(predicate) > 5;
而 Where(predicate).Skip(5).Any() 利用了 LINQ 的延迟执行。它会逐个检查元素,在找到第 6 个匹配项后立即返回 true,不再继续处理后面的元素。
当集合较大、匹配项较多,或者条件判断本身比较耗时时,提前结束遍历就可能带来明显收益。如果匹配项不超过 5 个,两种写法通常都需要遍历完整个序列。
Skip 的数量怎么写
可以记住下面的规律:
// 判断匹配数量是否大于 n
source.Where(predicate).Skip(n).Any();
// 判断匹配数量是否至少为 n(n > 0)
source.Where(predicate).Skip(n - 1).Any();
例如:
// 大于 5 个:确认第 6 个是否存在
source.Where(predicate).Skip(5).Any();
// 至少 5 个:确认第 5 个是否存在
source.Where(predicate).Skip(4).Any();
需要注意的地方
- 如果只是判断
List<T>的总数量,直接使用list.Count属性即可;本文讨论的是带筛选条件的数量判断。 - 谓词最好是无副作用的纯判断,因为
Any()可能提前结束,谓词不一定会对所有元素执行。 - 对 EF Core 等
IQueryable查询,最终性能还取决于生成的 SQL、索引和执行计划,必要时应结合真实数据测试。
LINQ使用Skip进行数量判断提速
https://dyhcad.cn/archives/01a03ce1-fbfa-7298-8d4c-9e2406f57f2f
评论