Go roaring 怎样用范围操作加入连续整数
来源:17golang原创
时间:2026-10-31 08:43:51 211浏览 收藏
Go roaring 怎样用范围操作加入连续整数
Go roaring 可以用 bm.AddRange(start, end) 一次加入连续整数,区间为左闭右开 [start, end)。例如加入100到109应写 AddRange(100, 110)。它修改当前位图,保留原有元素,重复添加已有整数不会产生重复项。
项目官方地址:https://github.com/RoaringBitmap/roaring。本文固定使用无 /v2 后缀的 v1.9.4 模块,示例讨论32位 Bitmap。下面提供读者可自行运行的小实验,本文没有执行这些代码,也没有提供性能实测数据。
准备依赖并确定半开区间
在已有 Go module 的目录中安装本文版本,把后面的完整示例保存为 main.go。若项目已采用其他主版本,应以对应版本的导入路径和文档为准,不要把不同主版本的代码混用。
# 在已有Go模块中固定本文使用的版本 go get github.com/RoaringBitmap/roaring@v1.9.4 # 保存后面的main.go后运行小集合示例 go run .
AddRange 的两个参数是 uint64,但 Bitmap 的元素仍是 uint32。参数较宽是为了容纳右端点 2^32:要包含最大元素4294967295,排除式右端点就需要4294967296。不能据此认为32位 Bitmap 能保存任意64位整数。

在小集合上加入连续整数
package main
import (
"fmt"
"github.com/RoaringBitmap/roaring"
)
func main() {
bm := roaring.New()
// 加入100至109,不包含右端点110。
bm.AddRange(100, 110)
fmt.Println("数量:", bm.GetCardinality())
fmt.Println("包含100:", bm.Contains(100))
fmt.Println("包含110:", bm.Contains(110))
// 与已有范围重叠时,重复元素不会增加计数。
bm.AddRange(105, 115)
fmt.Println("合并后的数量:", bm.GetCardinality())
// 仅对这个小集合展开;大范围不要直接ToArray。
fmt.Println("元素:", bm.ToArray())
}
按区间语义推导,第一次添加得到10个整数,100存在、110不存在;第二次添加后,集合覆盖100至114,共15个整数。这里的数字是集合计算的预期值,不是本机终端输出。重复部分105至109只计一次,不应把两次区间长度直接相加。
小实验可以展开 ToArray 观察元素,但大范围不要照搬这个展示方式。位图的压缩表示和把所有元素复制到切片是两件事;大量连续整数即使能紧凑表示,展开后仍需要为每个元素分配空间。
处理闭区间和uint32最大值
如果上游业务给的是“从 first 到 last 都包含”,要把闭区间转换成半开区间。不能先在 uint32 上执行 last+1,再转换成 uint64:当 last 是最大 uint32 时,加法已经溢出,后续转换无法挽回。
下面的辅助函数与上面的示例使用同一组导入,在 main 之外定义即可。错误由调用方接收处理。
// AddClosed 将uint32闭区间转换为AddRange使用的半开区间。
func AddClosed(bm *roaring.Bitmap, first, last uint32) error {
if bm == nil {
return fmt.Errorf("位图不能为nil")
}
if first > last {
return fmt.Errorf("闭区间起点大于终点")
}
// 先提升类型再加1,避免last为最大uint32时溢出。
bm.AddRange(uint64(first), uint64(last)+1)
return nil
}
例如 first 和 last 都为4294967295时,转换后是 [4294967295, 4294967296),只包含一个元素。不要为了观察这个边界而建立整个32位整数全集,更不要将全集调用 ToArray 展开。
| 业务要求 | 传给AddRange的范围 | 含义 |
|---|---|---|
| 100至109 | [100,110) | 10个整数 |
| 只加入7 | [7,8) | 1个整数 |
| 空区间 | [7,7) | 不新增元素 |
| 最大uint32元素 | [4294967295,4294967296) | 右端点需要uint64表达 |
把外部范围封装成明确错误
v1.9.4 的 AddRange 没有 error 返回值;实现对起点不小于终点的情况直接返回,对非空越界范围可能触发 panic。面向外部输入时,建议在应用边界明确检查,再把合法参数交给库。
// AddHalfOpen 校验外部传入的uint64半开区间。
func AddHalfOpen(bm *roaring.Bitmap, start, end uint64) error {
const limit uint64 = 1 end || start > limit || end > limit {
return fmt.Errorf("范围必须满足0
这个函数选择把反向区间作为错误,与库的静默返回行为有意区分。它还接受位于上界的合法空区间,但不接受超过上界的参数。若输入原本是有符号整数,必须在转换成 uint64 前检查负数;上面的函数接收无符号值,不负责恢复转换前的原始含义。

扩展实验与使用边界
可在独立小位图上尝试三组实验:重复加入同一范围,观察元素数量保持不变;加入相邻范围,观察边界两侧的 Contains;加入有重叠的范围,用并集计算预期数量。每组都重新创建位图,避免前一组数据影响判断。这里只使用内存对象,没有文件或网络资源需要关闭。
AddRange会先清空已有内容吗?不会,它是添加操作。需要新集合时创建新 Bitmap,避免误把添加理解为替换。
AddRange和Flip能互换吗?不能。Flip会翻转范围内成员的存在状态,已存在的值可能被删除;重复调用的含义与添加不同。
连续范围一定更省内存吗?不要把“连续”直接变成具体内存或速度承诺。RunOptimize 可尝试优化连续段的表示,但不改变集合成员;实际收益要按数据分布和应用负载衡量。本文不编造优化前后的数字。
批量加入连续整数时,最关键的是先统一半开区间约定、保持端点运算的类型宽度,并在业务入口处理非法范围。这样既能减少逐个构造元素的代码,也能把常见的末端漏值和溢出问题限制在清晰的函数边界内。
参考:v1.9.4接口文档;范围操作实现。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
172 收藏
-
303 收藏
-
227 收藏
-
212 收藏
-
421 收藏
-
319 收藏
-
401 收藏
-
469 收藏
-
200 收藏
-
443 收藏
-
216 收藏
-
398 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习