You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 06:30:40