如何用LINQ查询语法(含Contains)筛选未加入任何班级的学生?
问题解答
1. 用LINQ查询语法实现筛选无班级学生
你可以直接将方法语法中的Any逻辑迁移到查询语法中,实现和Option A完全一致的需求,代码如下:
var studentsWithoutClassRoom = from stu in students where !classes.Any(r => r.Students.Contains(stu)) select stu;
你之前的Option B结果不符合预期,是因为它执行了学生列表和班级列表的笛卡尔积(交叉连接):每个学生都会和每个班级逐一匹配,只要班级里没有该学生就返回这个班级,最终得到的是重复的班级对象集合,完全偏离了“筛选无班级学生”的目标。
2. LINQ查询语法连接列表的最快方式
要提升这类存在性检查的性能,核心是降低查找操作的时间复杂度。推荐先将所有已注册班级的学生存入HashSet<T>(HashSet的Contains操作是O(1)时间复杂度,远快于List的O(n)),再用查询语法筛选未注册的学生:
步骤1:提取所有已注册学生到HashSet
var enrolledStudents = new HashSet<Student>( from c in classes from s in c.Students select s );
步骤2:用查询语法筛选无班级学生
var studentsWithoutClassRoom = from stu in students where !enrolledStudents.Contains(stu) select stu;
性能说明
- 原Option A的时间复杂度:
O(S × (C × K)),其中S是学生总数,C是班级数,K是单个班级的学生数。每个学生都要遍历所有班级,每个班级还要遍历学生列表做Contains检查。 - 用HashSet的方式时间复杂度:
O(C×K + S),先遍历所有班级的学生存入HashSet(O(C×K)),再逐个检查学生是否在HashSet中(每个检查O(1),总O(S)),大数据量下性能提升非常明显。
注意:如果你的
Student类没有重写Equals和GetHashCode,HashSet和List的Contains会默认按引用相等判断(即只有同一个对象实例才会被判定为相等),这和你示例代码的逻辑一致。如果需要按属性(比如学生姓名)判断相等,需要重写这两个方法或实现IEqualityComparer<Student>并传入HashSet的构造函数。
内容的提问来源于stack exchange,提问作者niks
相关产品推荐
相关产品推荐

