在Lambda表达式的Where子句中使用Select是否存在性能损耗?
你的假设完全正确,第一种写法和第三种写法的性能完全不一致,第三种的性能远优于前两种,具体原因如下:
第一种写法的性能问题
var eventsToBeCreated = requiredEventDates.Where(d => !events.Select(e => e.eventDay).Contains(d));
在这个写法里,Where的每个元素判断逻辑都会完整执行一遍events.Select(e => e.eventDay),再调用Contains遍历这个新生成的序列。假设requiredEventDates有M个元素,events有N个元素,那么总操作次数是M*N——每检查一个日期,就要重新遍历一次所有事件提取日期,再遍历这个临时序列做匹配。数据量越大,性能下降越明显。
第二种写法的本质问题
如果你只是提前定义IEnumerable<DateTime> existingEventDays = events.Select(e => e.eventDay);,再用Where(d => !existingEventDays.Contains(d)),性能和第一种几乎没有区别。因为IEnumerable是延迟执行的,每次调用Contains时,都会重新触发Select的逻辑,再次遍历events生成日期序列,本质还是重复执行了M次Select+遍历。
第三种写法的性能优势
var existingEventDays = events.Select(e => e.eventDay).ToList(); var eventsToBeCreated = requiredEventDates.Where(d => !existingEventDays.Contains(d));
调用ToList()后,会立即执行Select逻辑并将结果缓存到List集合中。后续的Contains判断只会在这个已缓存的List里做线性查找,而不会再去遍历原始的events集合。总操作次数变成N + M*K(K是List的平均查找长度,远小于N),性能提升非常显著。
额外优化建议
如果events的数量很大,还可以把ToList()换成ToHashSet():
var existingEventDays = events.Select(e => e.eventDay).ToHashSet();
HashSet的Contains操作是O(1)的时间复杂度,能进一步降低查找开销,适合处理大规模数据集合。
内容的提问来源于stack exchange,提问作者Patric

