最易懂的 BitSet 详解


本文基于 JDK 1.8 来分析 Java 中 BitSet 的实现原理。

成员变量

BitSet 的作用是用每一个比特位(bit)来标记元素是否出现的,成员变量 words 是个 long 类型的数组,在 Java 中,long 类型的元素占 8 个字节(byte),一个字节占用 8 个 比特位。那么 words 数组中每一个元素可以标记 64 个值,假设数组长度为 L,那么整个 BitSet 可以标记的值数量就是 64*L。

private final static int ADDRESS_BITS_PER_WORD = 6;

private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD; // 01 00 00 00

private final static int BIT_INDEX_MASK = BITS_PER_WORD - 1; // 00 11 11 11

/* Used to shift left or right for a partial word mask */
private static final long WORD_MASK = 0xffffffffffffffffL;

private long[] words;

// 用来表示 words 数组中不为 0 的元素个数。注意这个值不一定等于 words 数组长度的值,因为数组扩容后会有一部分的下标未被使用。
private transient int wordsInUse = 0;

构造函数

构造函数中调用了initWords方法,它实例化了words数组,那么这个数组的长度是多少呢?BitSet有两个构造函数,如果使用不带形参的构造方法。那么数组的初始长度就是 1 ,即默认可以标记 64 个值。使用带形参的方法可以指定要使用多少个 bit 位数,你可以随便指定一个 >= 0 的整数值作为形参,但是指定的比特位数最终都会向上取整,得到一个64倍数的值。比如你指定 16,那么返回的 bit 位数是64,指定125,返回的 bit 位数是 128,以此类推。数组的长度即为比特位数/64。

public BitSet() {
    initWords(BITS_PER_WORD);
    sizeIsSticky = false;
}

public BitSet(int nbits) {
    // nbits can't be negative; size 0 is OK
    if (nbits < 0)
        throw new NegativeArraySizeException("nbits < 0: " + nbits);

    initWords(nbits);
    sizeIsSticky = true;
}

private void initWords(int nbits) {
    words = new long[wordIndex(nbits-1) + 1];
}

// 低 6 位 置 0
private static int wordIndex(int bitIndex) {
    return bitIndex >> ADDRESS_BITS_PER_WORD;
}

常用的函数

set

set 设置对应的 bit 位的值为1,即置 1 操作。比如 set(100),表示设置第 100 个 bit 为 1。转化到 words 数组中对应的位置就是下标为 1 处的第 36 个比特位。

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

    int wordIndex = wordIndex(bitIndex);
    expandTo(wordIndex);

    words[wordIndex] |= (1L << bitIndex); // Restores invariants

    checkInvariants();
}

private void expandTo(int wordIndex) {
    int wordsRequired = wordIndex+1;
    if (wordsInUse < wordsRequired) {
        ensureCapacity(wordsRequired);
        wordsInUse = wordsRequired;
    }
}

上面的代码中,expandTo 函数用来检查数组容量是否足够容纳将要 set 的元素大小,如果不足以容纳,则扩充数组的容量,扩充后的容量大小可能是原数组的2倍,也可能是将要 set 元素所在新 words 数组中的下标 + 1,这取决于你将要 set 的元素大小。扩容函数执行完之后就是真正的 set 操作了,通过逻辑或运算 | 和 左移运算符 << 可以在 words 数组对应下标值的对应比特位 置 1。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值