登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

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位整数。

AddRange半开区间与32位整数域的静态关系
结构图:起点包含、终点排除;uint64端点用于表达uint32元素域的边界。

在小集合上加入连续整数

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 前检查负数;上面的函数接收无符号值,不负责恢复转换前的原始含义。

已有元素与范围成员在同一位图内去重的静态集合关系
说明图:AddRange添加范围成员并保留原集合,重叠元素在结果集合中只出现一次。

扩展实验与使用边界

可在独立小位图上尝试三组实验:重复加入同一范围,观察元素数量保持不变;加入相邻范围,观察边界两侧的 Contains;加入有重叠的范围,用并集计算预期数量。每组都重新创建位图,避免前一组数据影响判断。这里只使用内存对象,没有文件或网络资源需要关闭。

AddRange会先清空已有内容吗?不会,它是添加操作。需要新集合时创建新 Bitmap,避免误把添加理解为替换。

AddRange和Flip能互换吗?不能。Flip会翻转范围内成员的存在状态,已存在的值可能被删除;重复调用的含义与添加不同。

连续范围一定更省内存吗?不要把“连续”直接变成具体内存或速度承诺。RunOptimize 可尝试优化连续段的表示,但不改变集合成员;实际收益要按数据分布和应用负载衡量。本文不编造优化前后的数字。

批量加入连续整数时,最关键的是先统一半开区间约定、保持端点运算的类型宽度,并在业务入口处理非法范围。这样既能减少逐个构造元素的代码,也能把常见的末端漏值和溢出问题限制在清晰的函数边界内。

参考:v1.9.4接口文档;范围操作实现。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>