JavaScript中位运算不能用于通用数组去重,仅适用于0–31(或0–63)的非负整数场景:用单个number或BigInt作位图标记已见数字,时间复杂度O(n)、空间极省;但不支持字符串、对象、浮点数、负数、NaN等,属高度受限特例,日常应优先使用Set。

JavaScript 中位运算本身不能直接用于通用数组去重,因为位运算(如 &、|、^、、<code>>>)只对32 位有符号整数有效,且仅适用于范围有限、非负、小于 2³¹ 的整数。它无法处理字符串、对象、浮点数、负数、NaN、null、undefined 等——而这些在真实数组中极其常见。
所以,“用位运算技巧加速数组去重”这个说法,不是一种普适方案,而是一个高度受限的优化特例:仅当你的数组满足以下全部条件时,才可考虑:
- 元素全是 0 到 31(或最多 32)之间的整数(便于用单个 32 位整数的每一位做标记);
- 或元素是 0 到 63 之间的整数(可用两个
BigInt位或一个BigInt,但需 ES2020+); - 且你明确接受「仅支持小范围整数」这一强约束。
下面分两种典型可行场景说明,讲清原理、写法、边界和为什么它“快”,也说清为什么绝大多数情况不该用。
适用场景一:0–31 小整数,用单个 number 作位图
原理:用一个 32 位整数 seen 的每一位(bit)代表一个数字是否出现过。例如第 0 位为 1 表示数字 0 已见,第 5 位为 1 表示数字 5 已见。
立即学习“Java免费学习笔记(深入)”;
function uniqueSmallInts(arr) {
const seen = 0; // 初始化为 0,所有位都是 0
const result = [];
for (const n of arr) {
// 确保 n 是 0–31 的合法整数
if (!Number.isInteger(n) || n < 0 || n > 31) continue;
const bit = 1 << n; // 把 1 左移 n 位,得到掩码
if (!(seen & bit)) { // 检查第 n 位是否为 0
result.push(n);
seen |= bit; // 把第 n 位设为 1
}
}
return result;
}⚠️ 注意:上面代码有 bug —— seen 是 const,不可变!实际必须用 let seen = 0。修正版:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
function uniqueSmallInts(arr) {
let seen = 0;
const result = [];
for (const n of arr) {
if (Number.isInteger(n) && n >= 0 && n <= 31) {
const bit = 1 << n;
if (!(seen & bit)) {
result.push(n);
seen |= bit;
}
}
}
return result;
}
// 示例
console.log(uniqueSmallInts([1, 2, 2, 3, 1, 5])); // [1, 2, 3, 5]✅ 优势:
- 时间复杂度 O(n),每次判断和设置都是常数时间(CPU 级位操作);
- 空间只用 1 个整数变量,无哈希表开销;
- 比
Set在该极端场景下略快(微秒级差异,仅百万级数据可测出)。
❌ 局限:
- 一旦
n = 32,1 在 JS 中等于 <code>1(溢出回绕),逻辑崩溃; -
n = -1、n = 3.14、n = "2"都会出错或被跳过; - 无法保留原始顺序?不,它天然保序(按遍历顺序首次出现即录入)。
适用场景二:0–63 整数,用 BigInt 位图(ES2020+)
BigInt 支持任意位宽,可安全覆盖 0–63:
function unique0to63(arr) {
let seen = 0n; // BigInt 字面量
const result = [];
for (const n of arr) {
if (Number.isInteger(n) && n >= 0 && n <= 63) {
const bit = 1n << BigInt(n);
if (!(seen & bit)) {
result.push(n);
seen |= bit;
}
}
}
return result;
}✅ 支持到 63,更宽松;
❌ 仍不支持非整数、负数、超界值;BigInt 运算略慢于 number 位运算,但远快于 Set 查找(对纯整数而言)。
为什么其他方法不能靠位运算加速?
- 字符串
"1"和数字1在位运算中无法统一处理("1" 会转成 <code>1,丢失类型信息); - 对象
{id:1}无法映射到位索引——没有自然、唯一、稠密、小范围的整数 ID 时,位图就失效; -
Set内部其实也用了哈希表(类似位桶),但它是通用、安全、已高度优化的;手动位图只是把“哈希→桶索引”的过程硬编码成“值→位偏移”,牺牲通用性换极窄场景的理论峰值性能。
简单说:位运算是“手工造轮子”,Set 是“出厂轮胎”。除非你确定赛道只有 32 块砖、每块砖编号 0–31,否则没必要自己钉钉子。
不复杂但容易忽略。

















