基于JavaScript Set实现交集、并集和差集运算代码

作者:纸飞机

时间:2026-10-30

浏览:622

详细讲解在JavaScript中基于原生Set对象实现并集、交集、差集与对称差集的实现原理、代码示例、ES2024规范新方法以及在实际开发中的性能优化与注意事项。

文章目录

在日常前端与 Node.js 业务开发中,数据去重、用户标签匹配、权限交叉判定以及状态列表差分等需求十分常见。JavaScript 在 ES6(ECMAScript 2015)中引入了原生的 Set 数据结构,它能够自动去重并提供平均复杂度为 O(1) 的元素查找能力。本文将完整介绍如何基于 Set 及其扩展机制,实现数学集合论中的交集(Intersection)、并集(Union)、差集(Difference)和对称差集(Symmetric Difference),并说明 ES2024 原生集合方法的最新演进与性能优化考量。

基于JavaScript Set实现交集、并集和差集运算代码
图 1:四种集合运算逻辑示意及数学集合定义

一、JavaScript Set 的特性与基础准备

在 ES6 规范中,Set 是一组不包含重复值的键值集合。它的主要方法和属性如下:

  • add(value):向集合添加元素,若已存在则忽略。
  • has(value):检测元素是否存在于集合中,返回布尔值。此操作的平均时间复杂度为 O(1)。
  • delete(value):从集合中移除指定元素。
  • size:返回当前集合的元素个数。

因为原生的 Array.prototype.filter 和扩展运算符(Spread Operator)能够便捷地在数组与可迭代对象之间桥接,在过去很长一段时间内,开发者最常用的集合运算方案是:将 Set 转换为数组,通过 filter 结合 has 过滤,最终再构造为新的 Set。

二、传统集合运算代码实现与操作流程

下面给出最经典的通用函数实现。这些函数既适用于现代浏览器环境,也适用于各类基于 Node.js 的旧版与通用运行时。

基于JavaScript Set实现交集、并集和差集运算代码
图 2:交集运算在 JavaScript 运行时的处理流水线

1. 并集(Union)

并集表示包含属于集合 A 或集合 B 的所有元素。通过扩展运算符将两个 Set 展开至同一个数组中并传给 new Set(),即可利用其内部哈希机制完成去重:

function union(setA, setB) {
  return new Set([...setA, ...setB]);
}

// 示例
const a = new Set([1, 2, 3]);
const b = new Set([3, 4, 5]);
console.log(union(a, b)); // Set(5) { 1, 2, 3, 4, 5 }

2. 交集(Intersection)

交集表示同时存在于集合 A 和集合 B 中的元素集合。通过筛选 setA 中在 setB 也存在的项:

function intersection(setA, setB) {
  // 性能优化:遍历较小的集合,检查是否存在于较大的集合中
  const [smaller, larger] = setA.size < setB.size ? [setA, setB] : [setB, setA];
  return new Set([...smaller].filter(item => larger.has(item)));
}

// 示例
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
console.log(intersection(a, b)); // Set(2) { 2, 3 }

3. 差集(Difference)

差集(相对补集)表示属于集合 A 但不属于集合 B 的元素(记作 A \ B)。需要注意顺序敏感性:A \ B 与 B \ A 的结果通常不同。

function difference(setA, setB) {
  return new Set([...setA].filter(item => !setB.has(item)));
}

// 示例
const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5]);
console.log(difference(a, b)); // Set(2) { 1, 2 }
console.log(difference(b, a)); // Set(1) { 5 }

4. 对称差集(Symmetric Difference)

对称差集是指属于集合 A 或属于集合 B,但不同时属于两者的元素组合。它等价于 (A \ B) ∪ (B \ A),也可通过“从并集中移除交集”实现:

function symmetricDifference(setA, setB) {
  const diffA = [...setA].filter(x => !setB.has(x));
  const diffB = [...setB].filter(x => !setA.has(x));
  return new Set([...diffA, ...diffB]);
}

// 示例
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
console.log(symmetricDifference(a, b)); // Set(2) { 1, 4 }

三、ES2024 原生集合方法对比与演变

ECMAScript 2024 正式为 Set.prototype 增加了原生集合运算方法(TC39 Set Methods Proposal)。在 Node.js 22+、Chrome 122+、Safari 17+ 等现代环境中,已无需手动书写工具函数即可直接调用原生底层方法。

基于JavaScript Set实现交集、并集和差集运算代码
图 3:现代 JavaScript (ES2024) 原生方法与传统实现映射表

原生 API 调用示例如下:

const setA = new Set(['admin', 'editor', 'viewer']);
const setB = new Set(['viewer', 'auditor']);

// 原生交集
const commonRoles = setA.intersection(setB);
console.log(commonRoles); // Set(1) { 'viewer' }

// 原生并集
const allRoles = setA.union(setB);
console.log(allRoles); // Set(4) { 'admin', 'editor', 'viewer', 'auditor' }

// 原生差集
const exclusiveToA = setA.difference(setB);
console.log(exclusiveToA); // Set(2) { 'admin', 'editor' }

四、关键细节与实战陷阱

特别提醒:引用类型比较与 NaN 处理
Set 判断元素相等的标准是基于 SameValueZero 算法。这意味着基本类型(数字、字符串、布尔值)能正确匹配,且 NaN 与 NaN 视为相等;但针对对象、数组、函数等引用类型,只有指向同一内存地址时才视为相同元素。

例如以下代码中,两个内容完全相同的对象字面量在 Set 中不会被判定为相等:

const a = new Set([{ id: 1 }, { id: 2 }]);
const b = new Set([{ id: 1 }, { id: 3 }]);

// 结果依然包含两个 { id: 1 } 对象,因为内存引用不同
console.log(new Set([...a, ...b]).size); // 4

如果业务数据是对象数组,应当在运算前将对象提取为具有唯一标识的主键集合(例如 id 或 uuid),或者结合 Map 按照特定键值进行哈希归约运算。

五、多场景选择与全景总结

在工程实践中,选用哪种集合运算策略取决于代码运行的宿主环境与数据集体量:

  • 现代 Node.js 与高版本浏览器环境:优先采用原生 ES2024 原生方法(如 setA.intersection(setB)),代码可读性最高且避免中间临时数组的内存垃圾回收(GC)开销。
  • 兼容旧宿主环境:使用展开运算符与 filter 结合的工具函数封装。交集操作务必加上“较小集合优先”判断,防止大集合无谓迭代。
  • 大数据量场景(超过 10 万级):避免直接展开为巨型数组(可能触碰参数个数限制或内存突增),推荐使用 for...of 直接在循环中调用目标集合的 add 或 delete。
基于JavaScript Set实现交集、并集和差集运算代码
图 4:项目环境与集合运算技术方案选择指南

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章