前言
位数组(Bit Array)是一种数据结构,它主要用于高效地利用内存空间来存储大量布尔值(真/假,1/0)。在位数组中,每个元素不再是一个完整的字节(如在大多数常规数组中那样),而是一位,即二进制数的一个比特。这种紧凑的表示方式极大地节省了存储空间,尤其适合需要存储大量稀疏数据的场景。
位数组与普通数组的比较:
-
数据类型与存储:
- 普通数组:每个元素可以是任意基本数据类型(如整数、浮点数、字符等),或者是复合数据类型(如对象引用),并且每个元素占据相应类型的固定内存空间。
- 位数组:每个元素仅占一位(bit),通常通过包装在字节或其他更大的数据类型中来实现,以方便操作系统的内存寻址。
-
内存效率:
- 普通数组:存储效率较低,因为即使是表示简单的布尔值也需要至少一个字节。
- 位数组:存储效率极高,因为可以将多个布尔值压缩到一个字节中存储。
-
操作复杂性:
- 普通数组:直接访问和修改元素相对简单,因为每个元素都是独立寻址的。
- 位数组:访问和修改元素通常需要进行位操作(如位移、与、或、异或等),操作上相对复杂,但许多编程语言和库提供了封装好的API来简化这些操作。
-
适用场景:
- 普通数组:适用于各种类型的数据存储,特别是当元素类型复杂或需要直接访问时。
- 位数组:特别适合于需要存储大量布尔值的场景,如标记某些项的状态(是否存在、是否已访问等)、权限控制、集合运算(如布隆过滤器)等。
普通数组示例
在Java中,一个普通的数组是用来存储同种类型元素的连续内存区域。以下是一个存储整数的普通数组示例:
// 定义一个普通数组
int[] normalArray = new int[5];
// 初始化数组元素
normalArray[0] = 1;
normalArray[1] = 2;
normalArray[2] = 3;
// 访问数组元素
System.out.println(normalArray[0]); // 输出: 1
// 遍历数组
for (int i = 0; i < normalArray.length; i++) {
System.out.print(normalArray[i] + " ");
}
// 输出: 1 2 3
位数组示例(BitSet)
Java标准库并没有直接提供位数组的类,但可以通过BitSet
类来实现类似的功能,BitSet
是一种特殊的数据结构,用于高效存储位值。
import java.util.BitSet;
public class BitSetExample {
public static void main(String[] args) {
// 创建一个BitSet,初始容量为64位
BitSet bitSet = new BitSet();
// 设置某些位为true
bitSet.set(0); // 第0位
bitSet.set(1); // 第1位
bitSet.set(11); // 第11位
// 访问BitSet中的位
System.out.println(bitSet.get(0)); // 输出: true
System.out.println(bitSet.get(2)); // 输出: false
// 遍历BitSet
for (int i = 0; i < bitSet.size(); i++) {
if (bitSet.get(i)) {
System.out.print(i + " ");
}
}
// 输出: 0 1 11
}
}
位数组底层原理
我们都知道,现代计算机体系结构通常以字节(8位)作为最小寻址单位,既然位数组每一个元素是一个bit,那它是如何存储的?
答案就是位打包
:通过将多个比特位打包到一个字节(或者其他更大的数据类型,如int, long等)中来实现的。每个字节(或数据类型)被视为一个桶或容器,里面可以存放若干个布尔值。对每一位操作时,要使用位运算进行访问。
//Bitset中元素以long为单位存储
private long[] words;