C#怎么实现集合求交集?LINQ的Intersect与Except用法详解

来源:建站教程作者:缓存小熊猫头衔:程序员
导读:本期聚焦于小伙伴创作的《C#怎么实现集合求交集?LINQ的Intersect与Except用法详解》,敬请观看详情。两个List对象要快速筛选出共同元素或差异元素,手动写循环不仅繁琐还容易出错。C#在System.Linq命名空间下提供了Intersect与Except扩展方法,底层基于哈希集实现,能以接近O(n)的复杂度完成集合运算。Intersect返回同时存在于两个序列的项,Except则返回存在于第一个序列但不在第二个序列中的项,两者都依赖默认的相等比较器,处理自定义对象时需重写Equals与GetHashCode或传入自定义IEqualityComparer。文中通过整数与实体类示例演示具体调用方式,并分析延迟执行特性及内存占用注意点,帮助你在数据比对场景中写出简洁稳定的代码。

在C#开发中,经常需要对两个集合进行比对,找出共同部分或独有部分。LINQ提供的Intersect与Except方法可以用极少的代码完成这类集合运算,而不必手写嵌套循环。理解它们的执行逻辑和比较规则,能避免很多隐藏的数据错误。

C#怎么实现集合求交集?LINQ的Intersect与Except用法详解

Intersect方法的基本使用

Intersect用于返回两个序列中均存在的元素集合。它位于System.Linq命名空间,是IEnumerable<T>的扩展方法。调用时,第一个集合调用Intersect并传入第二个集合,最终结果为交集。

下面的例子展示两个整数列表求交集的过程。Intersect内部会先将第二个序列装入哈希集,再遍历第一个序列做包含判断,因此平均时间复杂度约为O(n+m)。

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        List<int> listA = new List<int> { 1, 2, 3, 4, 5 };
        List<int> listB = new List<int> { 4, 5, 6, 7 };

        // 求交集,结果为 4,5
        var common = listA.Intersect(listB);

        foreach (var item in common)
        {
            Console.WriteLine(item);
        }
    }
}

上述代码输出4和5,顺序以第一个序列为准。需要注意的是,Intersect返回的是延迟执行结果,只有在遍历时才会真正计算。如果希望立即获得结果,可以加上ToList或ToArray。

对于字符串或值类型,Intersect使用默认相等比较器即可。但如果序列元素为自定义类,直接使用Intersect可能得不到预期结果,因为默认比较的是引用地址。

Except方法的基本使用

Except用于返回存在于第一个序列但不存在于第二个序列的元素,即差集。它与Intersect互补,常用于权限比对、数据同步等场景。

以下示例求出listA中有而listB中没有的元素:

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        List<int> listA = new List<int> { 1, 2, 3, 4, 5 };
        List<int> listB = new List<int> { 4, 5, 6, 7 };

        // 求差集,结果为 1,2,3
        var onlyInA = listA.Except(listB);

        foreach (var item in onlyInA)
        {
            Console.WriteLine(item);
        }
    }
}

运行后输出1、2、3。Except同样基于哈希集实现,且只保留第一个序列中独有的项,不会包含第二个序列的独有项。若需要对称差集,可结合两次Except并使用Concat。

延迟执行特性在Except中同样存在。若后续listA或listB被修改,再次遍历结果时会反映修改,因此建议在确定数据不变后再 materialize 结果。

自定义对象的比对处理

当集合元素为自定义类时,Intersect与Except默认使用引用相等,往往不符合业务预期。此时应重写Equals与GetHashCode,或传入IEqualityComparer<T>。

下面定义一个产品类,并通过比较器按编号求交集:

using System;
using System.Collections.Generic;
using System.Linq;

class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
}

class ProductComparer : IEqualityComparer<Product>
{
    public bool Equals(Product x, Product y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (x == null || y == null) return false;
        return x.Id == y.Id;
    }

    public int GetHashCode(Product obj)
    {
        return obj.Id.GetHashCode();
    }
}

class Program
{
    static void Main()
    {
        var a = new List<Product>
        {
            new Product { Id = 1, Name = "笔" },
            new Product { Id = 2, Name = "本" }
        };
        var b = new List<Product>
        {
            new Product { Id = 2, Name = "本子" },
            new Product { Id = 3, Name = "尺" }
        };

        var same = a.Intersect(b, new ProductComparer());
        foreach (var p in same)
        {
            Console.WriteLine(p.Name);
        }
    }
}

上述代码输出“本”,因为编号2同时存在于两个集合。使用比较器的方式比重写类方法更灵活,尤其当同一类在不同场景需要不同比对规则时。

如果不想额外定义类,也可先用Select提取关键属性组成匿名序列,再调用Intersect,最后用Join回查完整对象,这样能减少样板代码。

性能与注意事项

Intersect与Except在空间上会缓存第二个序列的全部元素到哈希集,因此当第二个集合极大时需注意内存占用。若只需判断存在性且数据可流式处理,可考虑用HashSet手动实现以精细控制。

两者都不会去除第一个序列内部的重复项,若原序列含重复元素,结果中对应项也只出现一次,这是集合语义决定的。如需保留重复次数语义,应使用GroupBy自行计算交集多重集。

方法语义空序列行为
Intersect两序列共有任一为空则结果为空
Except前者有后者无后者为空则返回前者去重

在实际项目中,推荐将结果立即ToList以减少重复计算,并针对大数据量做分批处理,避免一次性构建超大哈希集导致内存峰值过高。

C#LINQ_IntersectLINQ_Except修改时间:2026-08-05 10:15:22

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。