Go roaring 原地集合运算与返回新位图有什么区别
来源:17golang原创
时间:2026-10-31 08:55:45 317浏览 收藏
Go roaring 里最容易被忽略的差别,不是 And 和 Or 的集合含义,而是结果写到哪里:a.And(b)、a.Or(b) 会修改接收者 a;roaring.And(a, b)、roaring.Or(a, b) 则返回一个新的 *Bitmap,适合保留原输入。要不要“原地”,取决于这两个位图是不是还要被别的逻辑继续使用。
- 接收者方法把交集或并集写回当前 Bitmap,调用前要确认它可以被消耗。
- 包级函数返回新的结果位图,适合保留输入、生成快照或把结果交给另一个模块。
- 共享指针场景先考虑
Clone,回归检查至少比较原输入、结果集合和空集合行为。
先锁定两个 API 的修改边界
官方文档对方法的描述很直接:(*Bitmap).And 计算交集并把结果存入当前 Bitmap,(*Bitmap).Or 计算并集并把结果存入当前 Bitmap。也就是说,点号左边的变量就是被修改的对象。
包级函数的形态不同:roaring.And(x1, x2) 和 roaring.Or(x1, x2) 返回 *Bitmap。从调用约定上,应把它们当成“用两个输入得到一个结果”,不要把结果自动理解成第一个参数的别名。需要把原对象当作只读输入时,优先使用这种表达。

用最小示例对比 And 和 Or
下面把同一组输入分别交给两种写法。示例重点不是打印格式,而是观察变量在调用之后是否仍代表原集合。
package main
import (
"fmt"
"github.com/RoaringBitmap/roaring"
)
func main() {
left := roaring.BitmapOf(1, 2, 3, 8)
right := roaring.BitmapOf(2, 3, 5)
// 接收者方法会把交集写回 left,调用后 left 只保留共同元素。
inPlace := roaring.BitmapOf(1, 2, 3, 8)
inPlace.And(right)
fmt.Println("原地交集:", inPlace.ToArray())
// 包级函数返回新的交集,left 和 right 仍可作为后续输入。
intersection := roaring.And(left, right)
fmt.Println("返回交集:", intersection.ToArray())
fmt.Println("left 仍是:", left.ToArray())
// Or 的选择规则相同:方法改接收者,包级函数留下输入并返回并集。
union := roaring.Or(left, right)
fmt.Println("返回并集:", union.ToArray())
}
在这个例子里,两种交集写法的集合内容可以相同,但对象生命周期不同:inPlace 被消耗成交集,left 仍然包含原始元素。并集也是同样的选择题;如果左侧位图代表缓存中的原始标签集合,直接调用 left.Or(right) 就会改变缓存语义。
处理共享指针与迁移风险
把原地方法从局部变量移到公共函数里,最容易出现“看起来只是优化,实际上改了调用方数据”的回归。Go 的 *Bitmap 传递的是对象指针:如果两个字段、缓存项或返回值指向同一个 Bitmap,那么对其中一个引用调用 And 或 Or,另一个引用看到的也是被修改后的内容。
需要原地算法的效率、但又不能改原对象时,先复制再修改:
func intersectForQuery(base, filter *roaring.Bitmap) *roaring.Bitmap {
// 复制输入后再原地计算,避免改变调用方持有的 base。
result := base.Clone()
result.And(filter)
return result
}
func mergeSnapshot(base, extra *roaring.Bitmap) *roaring.Bitmap {
// 快照需要保留 base,所以把并集写入副本而不是共享对象。
result := base.Clone()
result.Or(extra)
return result
}
如果没有明确的所有权协议,包级 roaring.And、roaring.Or 往往更容易读懂;如果已经约定“这个函数独占结果位图”,接收者方法可以减少一次中间对象表达。两者都不是绝对更快,选择应先服从对象所有权,再用基准测试比较。

建立回归检查和选择清单
迁移一段集合运算代码时,可以按下面的顺序检查,但不要把它误解成某个固定框架的测试模板:
| 问题 | 适合的写法 | 要观察的结果 |
|---|---|---|
| 左侧 Bitmap 可以被消费吗? | left.And(right) 或 left.Or(right) | 调用后左侧就是交集或并集 |
| 两个输入之后还要复用吗? | roaring.And(left, right) 或 roaring.Or(left, right) | 原输入保持可继续读取 |
| 需要统一返回一个新对象吗? | base.Clone() 后调用方法 | 修改集中在副本上 |
| 输入可能为空吗? | 先按业务决定空集语义,再检查 IsEmpty | 交集、并集的边界符合预期 |
测试时不要只断言最终结果。至少保留一份输入快照,在原地分支之后确认允许修改的对象发生了变化、禁止修改的对象没有变化;如果代码会在多个 goroutine 间共享 Bitmap,还要遵守库文档给出的同步要求,不能把“返回新位图”误当成对同一输入对象并发读写的通用保护。
常见问题
原地 And 和 roaring.And 的集合结果一定不同吗?
不一定。对相同输入,它们都可以得到交集;关键差异是结果写入当前对象还是返回新位图。
为了避免修改输入,是否必须先 Clone?
不一定。可以直接使用包级函数;如果业务更适合接收者方法,再先对需要保留的输入调用 Clone。
Or 也遵循同一条规则吗?
是。(*Bitmap).Or 把并集写回当前 Bitmap,roaring.Or 返回并集结果;迁移时检查的是对象所有权,而不是只看运算符名字。
-
109 收藏
-
386 收藏
-
169 收藏
-
355 收藏
-
384 收藏
-
200 收藏
-
337 收藏
-
135 收藏
-
172 收藏
-
193 收藏
-
351 收藏
-
320 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习