如何使用LINQ查找两个学生列表中课程完全匹配的学生
用LINQ实现学生课程完全匹配的查找方案
第一步:解决课程对象的相等比较问题
Course是引用类型,默认比较的是对象引用,但我们需要按Id和Name的内容判断课程是否一致,有两种实现方式:
方式1:重写Course类的Equals和GetHashCode
直接修改Course类,让它按内容做相等判断:
public class Course { public Guid Id { get; set; } public string Name { get; set; } public override bool Equals(object obj) { if (obj is not Course course) return false; return Id.Equals(course.Id) && string.Equals(Name, course.Name, StringComparison.Ordinal); } public override int GetHashCode() { return HashCode.Combine(Id, Name); } }
注:如果需要忽略课程名称的大小写,把
StringComparison.Ordinal改成StringComparison.OrdinalIgnoreCase即可。
方式2:自定义IEqualityComparer比较器
不想修改Course类的话,可以单独写一个比较器:
public class CourseEqualityComparer : IEqualityComparer<Course> { public bool Equals(Course x, Course y) { if (x == null && y == null) return true; if (x == null || y == null) return false; return x.Id == y.Id && string.Equals(x.Name, y.Name, StringComparison.Ordinal); } public int GetHashCode(Course obj) { return obj == null ? 0 : HashCode.Combine(obj.Id, obj.Name); } }
第二步:用LINQ实现匹配逻辑
核心要求是学生的课程数量相同,且每一门课程完全匹配(无额外课程),结合Where和HashSet.SetEquals可以高效实现:
基础实现(适合中小数据量)
// 初始化比较器(如果用方式1重写了Equals,这一步可省略) var courseComparer = new CourseEqualityComparer(); // 遍历列表A的每个学生,查找列表B中的匹配学生 foreach (var studentA in listA) { var matchingStudents = listB.Where(studentB => // 快速过滤:课程数量必须一致 studentB.Courses.Count == studentA.Courses.Count && // 用HashSet的SetEquals判断课程集合是否完全一致(顺序不影响) studentA.Courses.ToHashSet(courseComparer).SetEquals(studentB.Courses) ).ToList(); // 处理匹配结果,示例:输出匹配学生 Console.WriteLine($"学生【{studentA.Name}】在B中的匹配学生:"); foreach (var match in matchingStudents) { Console.WriteLine($" - {match.Name}"); } }
为什么用
SetEquals?它内部会高效检查两个集合的元素是否完全相同,比两次All+Contains的性能更好,尤其是课程数量较多时。
优化实现(适合大数据量)
如果列表A、B的学生数量庞大,基础实现的O(n*m)复杂度会影响性能,可以预先对B的学生按课程集合哈希值分组,减少后续比较次数:
var courseComparer = new CourseEqualityComparer(); // 预先将列表B的学生按课程集合哈希值分组 var bStudentGroups = listB .GroupBy(student => // 计算课程集合的哈希值(用每个课程的哈希异或得到) student.Courses.Aggregate(0, (totalHash, course) => totalHash ^ courseComparer.GetHashCode(course)) ) .ToDictionary(g => g.Key, g => g.ToList()); foreach (var studentA in listA) { // 计算当前学生课程集合的哈希值 var aCourseHash = studentA.Courses.Aggregate(0, (totalHash, course) => totalHash ^ courseComparer.GetHashCode(course)); // 先找哈希匹配的候选学生,再做精确比较(避免哈希冲突) if (bStudentGroups.TryGetValue(aCourseHash, out var candidateStudents)) { var matches = candidateStudents.Where(studentB => studentB.Courses.Count == studentA.Courses.Count && studentA.Courses.ToHashSet(courseComparer).SetEquals(studentB.Courses) ).ToList(); // 处理匹配结果 } }
注:哈希值可能存在冲突,必须在找到候选后做精确的
SetEquals验证,不能只靠哈希值判断。
关键说明
- 课程数量检查优先:这是最快速的过滤条件,数量不同的学生直接排除,避免后续集合比较。
- 集合比较不关心顺序:
SetEquals不要求课程顺序一致,只要元素完全匹配就返回true,符合需求。 - 性能优化点:
HashSet的查找是O(1)复杂度,比List.Contains的O(n)高效很多,优先用ToHashSet转换后再比较。
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

