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 已见。
function uniqueSmallInts(arr) {
const seen = 0; // 初始化为 0,所有位都是 0
const result = [];
for (const n of arr) {
// 确保 n 是 0–31 的合法整数
if (!Number.isInteger(n) || n 31) continue;
const bit = 1 <p>⚠️ 注意:上面代码有 bug —— <code>seen</code> 是 <code>const</code>,不可变!实际必须用 <code>let seen = 0</code>。修正版:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill9868" title="Java注释规范Skills"><img
src="https://img.php.cn/upload/skill/001/246/273/6adaf20dbae7c507.jpg" alt="Java注释规范Skills" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill9868" title="Java注释规范Skills" class="overflowclass">Java注释规范Skills</a>
<p class="overflowclass">当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。</p>
</div>
<a rel="nofollow" href="/xiazai/skill9868" title="Java注释规范Skills" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><pre class="brush:js;toolbar:false;">function uniqueSmallInts(arr) {
let seen = 0;
const result = [];
for (const n of arr) {
if (Number.isInteger(n) && n >= 0 && n <p>✅ 优势: </p>
- 时间复杂度 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 <p>✅ 支持到 63,更宽松;<br>
❌ 仍不支持非整数、负数、超界值;<code>BigInt</code> 运算略慢于 <code>number</code> 位运算,但远快于 <code>Set</code> 查找(对纯整数而言)。</p><h3>为什么其他方法不能靠位运算加速?</h3>
- 字符串
"1"和数字1在位运算中无法统一处理("1" 会转成 <code>1,丢失类型信息); - 对象
{id:1}无法映射到位索引——没有自然、唯一、稠密、小范围的整数 ID 时,位图就失效; -
Set内部其实也用了哈希表(类似位桶),但它是通用、安全、已高度优化的;手动位图只是把“哈希→桶索引”的过程硬编码成“值→位偏移”,牺牲通用性换极窄场景的理论峰值性能。
简单说:位运算是“手工造轮子”,Set 是“出厂轮胎”。除非你确定赛道只有 32 块砖、每块砖编号 0–31,否则没必要自己钉钉子。
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










