BitSet—位图

简介: BitSet—位图

🔎概念


位图

用某一位表示存储的状态

位图的适用场景

  • 海量数据
  • 数据为自然数(≥ 0)
  • 数据不重复

举个栗子🌰

以 byte[] 数组为例

byte[] 数组中元素的大小是 1 Byte(字节), 8 bit(比特位)

存在数组 int[] arr = {1, 4, 12, 20}

满足数据为自然数(≥ 0), 数据不重复

存入 byte[] 数组

  • 对于 byte[] bytes
  • bytes[0] 用于存储 0 ~ 7 之间的数字
  • bytes[1] 用于存储 8 ~ 15 之间的数字
  • bytes[2] 用于存储 16 ~ 23 之间的数字
  • 用某一位表示存储的状态
  • 用 bytes[0] 的第 0 ~ 7 位(比特位)表示是否存储 0 ~ 7 之间的数字
  • 用 bytes[1] 的第 0 ~ 7 位(比特位)表示是否存储 8 ~ 15 之间的数字
  • 用 bytes[2] 的第 0 ~ 7 位(比特位)表示是否存储 16 ~ 23 之间的数字

🔎位图的模拟实现


此处利用 byte[] 数组模拟实现位图

定义两个变量

public byte[] bytes表示位图

public int usedSize表示位图中的元素个数

无参的构造方法🍭

public ExBitSet() {
    this.bytes = new byte[1];
}

带参的构造方法🍭

n 表示位数

8 表示byte[] 数组的大小为1 Byte(字节), 8 bit(比特位)

n / 8 +1

(0 ~ 7 之间的数字 / 8 = 0, 但所需的大小为 1 字节 → 存储 0 ~ 7 之间的数字)

public ExBitSet(int n) {
    this.bytes = new byte[n / 8 + 1];
}

set()


set(int val)

将某一位设置为 1

(将 val 存储至位图中)

  • val 表示传入的数字, 在 byte[] 数组中根据该数字的值修改其对应下标的位
  • arrayIndex 表示 bytes 数组的下标
  • bitIndex 表示对应的位
public void set(int val) {
    if(val < 0) {
        throw new ValException("val 值小于0");
    }
    int arrayIndex = val / 8;
    if(arrayIndex > bytes.length) {
        bytes = Arrays.copyOf(bytes, arrayIndex + 1);
    }
    int bitIndex = val % 8;
    bytes[arrayIndex] |= 1 << bitIndex;
    usedSize++;
}

举个栗子🌰

val = 12

arrayIndex = 12 / 8 = 1(对应 byte[] 数组的下标为1 → bytes[1])

bitIndex = 12 % 8 = 4(对应的位数为4 → 下标为1, 第 4 位表示 1 * 8 + 4 = 12)

bytes[arrayIndex] |= 1 << bitIndex

将 bytes[1] 的第 4 位设置为 1, 表示存储了数字 12

0 | 0 = 0

0 | 1 = 1

1 | 0 = 1

1 | 1 = 1

get()


get(int val)

判断当前位是否为 1

(判断 val 在位图中是否存在)

  • val 表示传入的数字, 在 byte[] 数组中根据该数字的值修改其对应下标的位
  • arrayIndex 表示 bytes 数字的下标
  • bitIndex 表示对应的位
public boolean get(int val) {
    if(val < 0) {
        throw new ValException("val 值小于0");
    }
    int arrayIndex = val / 8;
    if(arrayIndex > bytes.length) {
         bytes = Arrays.copyOf(bytes, arrayIndex + 1);
    }
    int bitIndex = val % 8;
    return (bytes[arrayIndex] & 1 << bitIndex) != 0;
}

举个栗子🌰

val = 12

arrayIndex = 12 / 8 = 1(对应 byte[] 数组的下标为1 → bytes[1])

bitIndex = 12 % 8 = 4(对应的位数为4 → 下标为1, 第 4 位表示 1 * 8 + 4 = 12)

bytes[arrayIndex] &= 1 << bitIndex

若存储了数字12, 则 bytes[1] 的第 4 位为 1

0 & 0 = 0

0 & 1 = 0

1 & 0 = 0

1 & 1 = 1

reSet()


reSet(int val)

将某一位设置为 0

(将 val 从位图中删除)

  • val 表示传入的数字, 在 byte[] 数组中根据该数字的值修改其对应下标的位
  • arrayIndex 表示 bytes 数字的下标
  • bitIndex 表示对应的位
public void reSet(int val) {
    if(val < 0) {
        throw new ValException("val 值小于0");
    }
    int arrayIndex = val / 8;
    if(arrayIndex > bytes.length) {
        bytes = Arrays.copyOf(bytes, arrayIndex + 1);
    }
    int bitIndex = val % 8;
    bytes[arrayIndex] &= ~(1 << bitIndex);
    usedSize--;
}

举个栗子🌰

val = 12

arrayIndex = 12 / 8 = 1(对应 byte[] 数组的下标为1 → bytes[1])

bitIndex = 12 % 8 = 4(对应的位数为4 → 下标为1, 第 4 位表示 1 * 8 + 4 = 12)

bytes[arrayIndex] &= ~(1 << bitIndex)

将 bytes[1] 的第 4 位设置为 0, 表示删除了数字 12

~0 = 1

~1 = 0

getUsedSize()


getUsedSize()

获取位图中的元素个数

public int getUsedSize() {
    return usedSize;
}

完整代码


这里的异常是自定义的

也可以根据自己的需要进行相关的修改

import java.util.Arrays;
public class ExBitSet {
    public byte[] bytes;
    // 记录当前位图中存在多少个有效数据
    public int usedSize;
    public ExBitSet() {
        this.bytes = new byte[1];
    }
    /**
     * n → 需要多少位
     */
    public ExBitSet(int n) {
        this.bytes = new byte[n / 8 + 1];
    }
    /**
     * 设置某一位为 1
     */
    public void set(int val) {
        if(val < 0) {
            throw new ValException("val 值小于0");
        }
        int arrayIndex = val / 8;
        if(arrayIndex > bytes.length) {
            bytes = Arrays.copyOf(bytes, arrayIndex + 1);
        }
        int bitIndex = val % 8;
        bytes[arrayIndex] |= 1 << bitIndex;
        usedSize++;
    }
    /**
     * 判断当前位是否为 1
     */
    public boolean get(int val) {
        if(val < 0) {
            throw new ValException("val 值小于0");
        }
        if(arrayIndex > bytes.length) {
            bytes = Arrays.copyOf(bytes, arrayIndex + 1);
        }
        int arrayIndex = val / 8;
        int bitIndex = val % 8;
        return (bytes[arrayIndex] & 1 << bitIndex) != 0;
    }
    /**
     * 将对应位置变为0
     */
    public void reSet(int val) {
        if(val < 0) {
            throw new ValException("val 值小于0");
        }
        if(arrayIndex > bytes.length) {
            bytes = Arrays.copyOf(bytes, arrayIndex + 1);
        }
        int arrayIndex = val / 8;
        int bitIndex = val % 8;
        bytes[arrayIndex] &= ~(1 << bitIndex);
        usedSize--;
    }
    // 当前位图中的元素个数
    public int getUsedSize() {
        return usedSize;
    }
}

🔎利用位图进行排序


根据 byte[] 数组的下标计算数据所在的范围区间

根据当前位是否为 1 判断是否存储了该数字

  • bytes[0] 表示 0 ~ 7 之间的数字(0 * 8 + 对应的位)
  • bytes[1] 表示 8 ~ 15 之间的数字(1 * 8 + 对应的位)
  • bytes[2] 表示 16 ~ 23 之间的数字(2 * 8 + 对应的位)
public static void main(String[] args) {
   ExBitSet bitSet = new ExBitSet();
   int n = bitSet.bytes.length;
   for(int i = 0; i < n; i++) {
       int x = bitSet.bytes[i];
       for(int j = 0; x != 0 && j < 8; j++) {
           if((x & 1 << j) != 0) {
               System.out.println(i * 8 + j);
           }
       }
   }
}

举个栗子🌰

假设当前位图中存储的元素为 1, 12, 4, 0, 23, 5

  • int n = 3
  • bytes[0] != 0, bytes[1] != 0, bytes[2] != 0
  • i 表示对应的下标, i * 8 表示对应的取值范围
  • j 表示对应的位数, i * 8 + j 表示具体的数值

🔎结尾

创作不易,如果对您有帮助,希望您能点个免费的赞👍

大家有什么不太理解的,可以私信或者评论区留言,一起加油

相关文章
|
7月前
|
算法 固态存储 数据处理
位图(bitset)的使用【STL】
位图(bitset)的使用【STL】
24 1
|
5月前
|
存储 算法 Linux
C++ 哈希的应用【位图】
C++ 哈希的应用【位图】
28 0
|
7月前
|
C++ 容器
bitset(C++实现)
bitset(C++实现)
39 0
|
9月前
|
存储 C++ 容器
哈希的应用——位图
所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的,其本质也是一种hash。但是其占用空间很少。
|
算法 C++
|
存储 安全 索引
Bitmaps(位图)
什么是 Bitmaps Bitmaps 并不是实际的数据类型,而是定义在String类型上的一个面向字节操作的集合。因为字符串是二进制安全的块,他们的最大长度是512M,最适合设置成2^32个不同字节。 Bitmaps 的最大优势之一在存储信息时极其节约空间。例如,在一个以增量用户ID来标识不同用户的系统中,记录用户的四十亿的一个单独bit信息(例如,要知道用户是否想要接收最新的来信)仅仅使用512M内存。
 Bitmaps(位图)
|
存储 NoSQL Redis
Bitmaps-位图
Bitmaps-位图
131 0
Bitmaps-位图