BitSet的原理介绍

 

原理

众所周知,Java的BitSet使用一个Long(一共64位)的数组中的每一位(bit)是否为1来表示当前Index的数存在不。但是BitSet又是如何实现的呢?其实只需要理解其中的两个方法:

  • set
  • get

就能够理解BitSet的实现原理是什么了。

set

先看源代码:

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();
}

抛过判断小于0, 从第 4行开始读取

wordIndex

/**
 * Given a bit index, return word index containing it.
 */
private static int wordIndex(int bitIndex) {
    return bitIndex >> ADDRESS_BITS_PER_WORD;
}
private final static int ADDRESS_BITS_PER_WORD = 6;

这里ADDRESS_BITS_PER_WORD的值是6,那么最先想到的问题就是:为什么是6呢?而不是其他值呢?

答案其实很简单,还记得在最开始提到的:BitSet里使用一个Long数组里的每一位来存放当前Index是否有数存在。

因为在Java里Long类型是64位,所以一个Long可以存储64个数,而要计算给定的参数bitIndex应该放在数组(在BitSet里存在word的实例变量里)的哪个long里,只需要计算:bitIndex / 64即可,这里正是使用>>来代替除法(因为位运算要比除法效率高)。而64正好是2的6次幂,所以ADDRESS_BITS_PER_WORD的值是6。

通过wordIndex函数就能计算出参数bitIndex应该存放在words数组里的哪一个long里。

expandTo

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

从上面已经知道在BitSet里是通过一个Long数组(words)来存放数据的,这里的expandTo方法就是用来判断words数组的长度是否大于当前所计算出来的wordIndex(简单的说,就是能不能存的下),如果超过当前words数组的长度(记录在实例变量wordsInUse里),也即是存不下,则新加一个long数到words里(ensureCapacity(wordsRequired)所实现的。)。

Restores invariants

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

 这一行代码可以说是BitSet的精髓了,先不说什么意思,我们先看看下面代码的输出:

System.out.println(Integer.toBinaryString(1<<0));
System.out.println(Integer.toBinaryString(1<<1));
System.out.println(Integer.toBinaryString(1<<2));
System.out.println(Integer.toBinaryString(1<<3));
System.out.println(Integer.toBinaryString(1<<4));
System.out.println(Integer.toBinaryString(1<<5));
System.out.println(Integer.toBinaryString(1<<6));
System.out.println(Integer.toBinaryString(1<<7));

结果为:

 1
 10
 100
 1000
 10000
 100000
 1000000
 10000000

从而发现,上面所有的输出力,1 所在的位置,正好是第1,2,3,4,5,6,7,8(Java数组的Index从0开始)位。而BitSet正是通过这种方式,将所给的bitIndex所对应的位设置成1,表示这个数已经存在了。这也解释了(1L << bitIndex)的意思(注意:因为BitSet是使用的Long,所以要使用1L来进行位移)。

搞懂了(1L << bitIndex),剩下的就是用|来将当前算出来的和以前的值进行合并了words[wordIndex] |= (1L << bitIndex);

剩下的checkInvariants就没什么好解释的了。

get

搞懂了set方法,那么get方法也就好懂了,整体意思就是算出来所给定的bitIndex所对应的位数是否为1即可。先看看代码:

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

    checkInvariants();

    int wordIndex = wordIndex(bitIndex);
    return (wordIndex < wordsInUse)
        && ((words[wordIndex] & (1L << bitIndex)) != 0);
}

计算wordIndex在上面set方法里已经说明了,就不再细述。这个方法里,最重要的就只有:words[wordIndex] & (1L << bitIndex)。这里(1L << bitIndex)也已经做过说明,就是算出一个数,只有bitIndex位上为1,其他都为0,然后再和words[wordIndex]&计算,如果words[wordIndex]数的bitIndex位是0,则结果就是0,以此来判断参数bitIndex存在。

贴上API

    • Modifier and TypeMethod and Description
      voidand(BitSet set)

      执行此参数位置位的此目标位设置的逻辑 AND

      voidandNot(BitSet set)

      清除所有的位,这 BitSet其相应的位被设置在指定的 BitSet

      intcardinality()

      返回此 BitSet设置为 trueBitSet

      voidclear()

      将此BitSet中的所有位设置为 false

      voidclear(int bitIndex)

      将索引指定的位设置为 false

      voidclear(int fromIndex, int toIndex)

      将指定的 fromIndex (含)的位设置为 toIndex (排他)到 false

      Objectclone()

      克隆这个 BitSet产生一个新的 BitSet等于它。

      booleanequals(Object obj)

      将此对象与指定对象进行比较。

      voidflip(int bitIndex)

      将指定索引处的位设置为其当前值的补码。

      voidflip(int fromIndex, int toIndex)

      将指定的每一位 fromIndex (含)到指定的 toIndex (独家)为其当前值的补码。

      booleanget(int bitIndex)

      返回具有指定索引的位的值。

      BitSetget(int fromIndex, int toIndex)

      返回一个新 BitSet组成位从这个 BitSetfromIndex (含)至 toIndex (独家)。

      inthashCode()

      返回此位集的哈希码值。

      booleanintersects(BitSet set)

      如果指定,则返回true BitSet具有设置为任何位 true这也被设置为 true这个 BitSet

      booleanisEmpty()

      如果此 BitSet包含设置为 true位,则返回true。

      intlength()

      返回这个 BitSet的“逻辑大小”: BitSet加上最高位的索引。

      intnextClearBit(int fromIndex)

      返回在指定的起始索引上或之后设置为 false的第一个位的索引。

      intnextSetBit(int fromIndex)

      返回在指定的起始索引上或之后发生的第一个位的索引设置为 true

      voidor(BitSet set)

      使用位设置参数执行该位的逻辑

      intpreviousClearBit(int fromIndex)

      返回被设置为最接近的位的索引 false上或指定的起始索引之前发生。

      intpreviousSetBit(int fromIndex)

      返回被设置为最接近的位的索引 true上或指定的起始索引之前发生。

      voidset(int bitIndex)

      将指定索引处的位设置为 true

      voidset(int bitIndex, boolean value)

      将指定索引处的位设置为指定值。

      voidset(int fromIndex, int toIndex)

      将指定的 fromIndex (含)的位设置为指定的 toIndex (排他)到 true

      voidset(int fromIndex, int toIndex, boolean value)

      将指定的 fromIndex (含)的位设置为指定值的 toIndex (排除)。

      intsize()

      返回此 BitSet实际使用的空间位数,以表示位值。

      IntStreamstream()

      返回此 BitSet包含设置状态位的索引流。

      byte[]toByteArray()

      返回一个包含该位集中所有位的新字节数组。

      long[]toLongArray()

      返回一个包含该位集合中所有位的新长数组。

      StringtoString()

      返回此位集的字符串表示形式。

      static BitSetvalueOf(byte[] bytes)

      返回包含给定字节数组中所有位的新位集合。

      static BitSetvalueOf(ByteBuffer bb)

      返回一个新的位集,其中包含给定字节缓冲区中位置和极限之间的所有位。

      static BitSetvalueOf(long[] longs)

      返回包含给定长数组中所有位的新位集。

      static BitSetvalueOf(LongBuffer lb)

      返回包含给定长缓冲区中其位置和极限之间的所有位的新位集合。

      voidxor(BitSet set)

      使用位设置参数执行该位的逻辑 异或

`std::bitset` 是 C++ 标准库中提供的一个类模板,用于表示固定大小的二进制序列。它的原理是使用一个整数类型(通常是无符号整数)来存储二进制数据,并提供了一组成员函数和操作符来对二进制数据进行各种操作。 `std::bitset` 类模板的基本原理如下: 1. 内部存储:`std::bitset` 使用一个整数类型的变量来存储二进制数据。这个整数变量的大小由模板参数指定,例如 `std::bitset<N>` 中的 `N` 指定了位数。通常情况下,整数类型是无符号整数,如 `unsigned long long`。 2. 位级操作:`std::bitset` 提供了一组成员函数和操作符,用于对二进制数据进行位级操作。这些操作包括按位与(`&`)、按位或(`|`)、按位异或(`^`)、按位取反(`~`)等。通过这些操作,你可以对 `std::bitset` 中的二进制数据进行位级运算。 3. 数值转换:`std::bitset` 支持将二进制数据转换为整数类型,以及将整数类型转换为二进制数据。你可以使用 `to_ulong()` 成员函数将 `std::bitset` 转换为无符号整数,或使用 `to_ullong()` 将其转换为无符号长长整数。相反,你可以使用 `std::bitset` 的构造函数将整数类型转换为 `std::bitset`。 4. 迭代器支持:`std::bitset` 支持迭代器,使你能够对 `std::bitset` 的每个位进行迭代访问。 通过这些原理和功能,`std::bitset` 提供了一种便捷的方式来处理和操作固定大小的二进制数据。 希望这能解答你的问题!如果还有其他疑问,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值