有關bitmap算法的介紹資料網上很多,這里不贅述,各種語言的實現也不少,但是Go語言版的bitmap不多,本文就來寫一個Go版的bitmap實現。
首先創建一個 bitmap.go 文件,定義一個bitmap結構體,再提供一些操作方法。詳細代碼如下:
package bitmap import ( "fmt" "strings" ) const ( bitSize = 8 ) var bitmask = []byte{1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7} // 首字母小寫 只能調用 工廠函數 創建 type bitmap struct { bits []byte bitCount uint64 // 已填入數字的數量 capacity uint64 // 容量 } // 創建工廠函數 func NewBitmap(maxnum uint64) *bitmap { return &bitmap{bits: make([]byte, (maxnum+7)/bitSize), bitCount: 0, capacity: maxnum} } // 填入數字 func (this *bitmap) Set(num uint64) { byteIndex, bitPos := this.offset(num) // 1 左移 bitPos 位 進行 按位或 (置為 1) this.bits[byteIndex] |= bitmask[bitPos] this.bitCount++ } // 清除填入的數字 func (this *bitmap) Reset(num uint64) { byteIndex, bitPos := this.offset(num) // 重置為空位 (重置為 0) this.bits[byteIndex] &= ^bitmask[bitPos] this.bitCount-- } // 數字是否在位圖中 func (this *bitmap) Test(num uint64) bool { byteIndex := num / bitSize if byteIndex >= uint64(len(this.bits)) { return false } bitPos := num % bitSize // 右移 bitPos 位 和 1 進行 按位與 return !(this.bits[byteIndex]&bitmask[bitPos] == 0) } func (this *bitmap) offset(num uint64) (byteIndex uint64, bitPos byte) { byteIndex = num / bitSize // 字節索引 if byteIndex >= uint64(len(this.bits)) { panic(fmt.Sprintf(" runtime error: index value %d out of range", byteIndex)) return } bitPos = byte(num % bitSize) // bit位置 return byteIndex, bitPos } // 位圖的容量 func (this *bitmap) Size() uint64 { return uint64(len(this.bits) * bitSize) } // 是否空位圖 func (this *bitmap) IsEmpty() bool { return this.bitCount == 0 } // 是否已填滿 func (this *bitmap) IsFully() bool { return this.bitCount == this.capacity } // 已填入的數字個數 func (this *bitmap) Count() uint64 { return this.bitCount } // 獲取填入的數字切片 func (this *bitmap) GetData() []uint64 { var data []uint64 count := this.Size() for index := uint64(0); index < count; index++ { if this.Test(index) { data = append(data, index) } } return data } func (this *bitmap) String() string { var sb strings.Builder for index := len(this.bits) - 1; index >= 0; index-- { sb.WriteString(byteToBinaryString(this.bits[index])) sb.WriteString(" ") } return sb.String() } func byteToBinaryString(data byte) string { var sb strings.Builder for index := 0; index < bitSize; index++ { if (bitmask[7-index] & data) == 0 { sb.WriteString("0") } else { sb.WriteString("1") } } return sb.String() }
代碼中有注釋,很容易看懂。下面寫測試代碼,測試這個bitmap。
package main import ( "bitmap" "fmt" ) func main() { array := [...]uint64{0, 6, 3, 7, 2, 8, 1, 4} var maxNum uint64 = 9 bm := bitmap.NewBitmap(maxNum) for _, v := range array { bm.Set(v) } bm.Set(5) fmt.Println(bm.IsFully()) fmt.Println(bm.IsEmpty()) fmt.Println("bitmap 中存在的數字:") fmt.Println(bm.GetData()) fmt.Println("bitmap 中的二進制串") fmt.Println(bm.String()) fmt.Println("bitmap 中的數字個數:", bm.Count()) fmt.Println("bitmap size:", bm.Size()) fmt.Println("Test(0):", bm.Test(0)) bm.Reset(5) fmt.Println(bm.String()) fmt.Println("Test(5):", bm.Test(5)) fmt.Println(bm.GetData()) }
測試代碼的輸出如下:
true
false
bitmap 中存在的數字:
[0 1 2 3 4 5 6 7 8]
bitmap 中的二進制串
00000001 11111111
bitmap 中的數字個數: 9
bitmap size: 16
Test(0): true
00000001 11011111
Test(5): false
[0 1 2 3 4 6 7 8]
最后小結,bitmap的實現代碼可以根據需要添加其它方法,本文的實現僅供參考。另外文中的代碼不是線程安全的,多線程中使用需要改寫bitmap的代碼加上鎖。