Java BitSet学习

一. 位运算

在学习 BitSet 之前,我们先看一下位运算;

下述我们拿 long 来接收位运算的返回值,我们知道 long 为 64 位,8 个字节;

// 1. 右移一位,表示 num / 2;右移 6 位,表示 num / 2^6
// 10000     2^4
// 1000000   2^6
// 111111    2^6 - 1
long a = 64 >> 6;
System.out.println(a);  // 1

long b = 63 >> 6;
System.out.println(b);  // 0


// 2. 左移一位,表示 num * 2;左移 6 位,表示 num * 2^6
// 如果左移 70 位,超过 64 位会溢出,本质还是左移 6 位,也就是 num * 2^6
long c = 1 << 6;
System.out.println(c);  // 64

long d = 1 << 70;
System.out.println(d);  // 64

二. BitSet

1. 创建和初始化

可以通过无参构造器或指定初始容量的构造器创建 BitSet;

BitSet bs = new BitSet();   // 默认初始容量为 64 位
BitSet bs = new BitSet(10); // 指定初始容量为 10 位

BitSet 中是以 long[] 来存储位数的,所以 BitSet 的实际存储容量是以 64 的倍数增长的,因此,即使我们指定了 10 位的初始容量,BitSet 的实际容量也是 64 位;

2. 读取容量和长度

BitSet 提供了 size() 和 length() 来获取其实际存储容量和逻辑长度;

  • size():返回 BitSet 实际存储的位数,该值是 64 位的倍数;
  • length():返回 BitSet 中最高设置位的索引加 1,这表示逻辑上的长度;
BitSet bs = new BitSet(10);
int size = bs.size();
System.out.println("size = " + size);      // size = 64
int length = bs.length();
System.out.println("length = " + length);  // length = 0

bs.set(1);
bs.set(3);
bs.set(5);
bs.set(7, false);
length = bs.length();
System.out.println("length = " + length);  // length = 6

3. 设置和清除位

BitSet 中提供了很多方法来设置和清除位,包括 set() 和 clear();

BitSet bs = new BitSet();
bs.set(2);                  // 将第 2 位设为 true
bs.clear(2);                // 将第 2 位设为 false
bs.set(3, false);           // 将第 3 位设为 false

// 可以通过传递范围参数来批量设置或清除位,左闭右开
bs.set(0, 5);               // 将第 0 到第 4 位设为 true
bs.clear(0, 5);             // 将第 0 到第 4 位设为 false

4. 检查位状态

可以通过 get(int index) 来检查指定位的状态;

BitSet bitSet = new BitSet();

bitSet.set(0, true);
bitSet.set(1);
bitSet.set(2, false);

System.out.println(bitSet.get(1));    // true
System.out.println(bitSet.get(2));    // false

5. 其他常用方法

  1. isEmpty():判断 BitSet 是否为空;
  2. cardinality():获取 BitSet 中设置为 true 的位的数量;
  3. toString():将 BitSet 转换为字符串输出,只输出 true 的位;
BitSet bitSet = new BitSet();

bitSet.set(0, true);
bitSet.set(1);
bitSet.set(2, false);

System.out.println(bitSet.isEmpty());       // false
System.out.println(bitSet.cardinality());   // 2
System.out.println(bitSet.toString());      // {0, 1}

6. 遍历

6.1 遍历1位

BitSet 的 stream() 会返回一个 stream 流,其中包含了所有被设置为 1 的位的索引;

BitSet bitSet = new BitSet();

bitSet.set(1);
bitSet.set(3);
bitSet.set(7);

bitSet.stream().boxed().forEach(item -> System.out.println(item));  // 1 3 7

6.2 遍历0位

BitSet 的 nextClearBit(int index) 会返回大于或等于给定索引的第一个未设置位的位置;

BitSet 中没有现有的 api 轮询设置位为 0 的位的索引,需要结合 nextClearBit();

BitSet bitSet = new BitSet();

bitSet.set(1);
bitSet.set(3);
bitSet.set(7);

for (int i = bitSet.nextClearBit(0); 
     i < bitSet.size(); 
     i = bitSet.nextClearBit(i+1)) {
    System.out.println(i);
}

三. 源码分析

我们对几个比较常用的方法进行源码分析;

1. 构造方法

举个例子,如果 nbits 为 64,wordIndex(64-1) + 1 = 1;

如果 nbits 为 65,wordIndex(65-1) + 1 = 2;

// -------------------------------- BitSet ---------------------------------
public BitSet(int nbits) {
    if (nbits < 0)
        throw new NegativeArraySizeException("nbits < 0: " + nbits);

    // 初始化 long words[] 数组
    initWords(nbits);
    sizeIsSticky = true;
}


// -------------------------------- BitSet ---------------------------------
private void initWords(int nbits) {
    // wordIndex(nbits-1) 计算 long words[] 数组的初始化大小
    words = new long[wordIndex(nbits-1) + 1];
}


// -------------------------------- BitSet ---------------------------------
private static int wordIndex(int bitIndex) {
    // 计算 bitIndex 需要的数组大小
    // 其实就是 bitIndex 右移 6 位,也就是 bitIndex / 2^6
    return bitIndex >> 6;
}

2. set()

// -------------------------------- BitSet ---------------------------------
public void set(int bitIndex) {
    if (bitIndex < 0)
        throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);

    // 1. 计算在 words[] 中的下标
    int wordIndex = wordIndex(bitIndex);
    expandTo(wordIndex);

    // 2. 作或运算
    // 如果 bitIndex = 70,wordIndex = 1,1 << 70 = 1000000
    words[wordIndex] |= (1L << bitIndex);

    checkInvariants();
}

3. get()

// -------------------------------- BitSet ---------------------------------
public boolean get(int bitIndex) {
    if (bitIndex < 0)
        throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);

    checkInvariants();

    // 1. 计算在 words[] 中的下标
    int wordIndex = wordIndex(bitIndex);
    
    // 2. 不能超出 wordsInUse,作与运算
    return (wordIndex < wordsInUse)
        && ((words[wordIndex] & (1L << bitIndex)) != 0);
}

4. size()

// -------------------------------- BitSet ---------------------------------
public int size() {
    // long words[] 数组的长度 * 64,其实就是得到位的长度
    return words.length * 64;
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值