在Java开发中,我们一般使用boolean类型来存储布尔值(true/false),但是我们有没有想过一个boolean类型的值会占用1个byte的内存,但是其实我们只需要1bit的内存就可以表示true/false了,也就是说我们有很大部分内存浪费了,如果是大量存储的话,那浪费的内存数量就很可观了。
其实Java SDK中提供了BitSet来优化这个问题,只使用1bit来表示true/false,下面就来思考一下它是如何实现的。首先,在Java中是没有只占用1bit的数据类型,所以只能使用byte
/int
/short
等类型来存储,再通过位运算来操作某个bit位,简单起见,我们使用byte
类型来举例说明。一个byte类型占8bit,我们用一个byte来存储8个标识位,默认设置8个标识位都为0
byte seg = 0
seg
对应8个bit位存储的值为
0
0
0
0
0
0
0
0
如果要设置index=3的标识位为1,需要使用左移位运算<<
和按位或运算|
来实现
seg |= 1 << 3
执行完之后,seg
对应8个bit位存储的值为
0
0
0
0
1
0
0
0
如果要设置index=7的标识位为1,执行类似的操作
seg |= 1 << 7
执行完之后,seg
对应8个bit位存储的值为
1
0
0
0
1
0
0
0
假设当前seg
对应8个bit位存储的值为
1
0
0
0
1
0
0
0
如果要清除index=3的标识位,需要使用左移位运算<<
、按位非运算~
和按位与运算&
来实现
seg &= ~(1 << 3)
执行完之后,seg
对应8个bit位存储的值为
1
0
0
0
0
0
0
0
如果要清除index=7的标识位,执行类似的操作
seg &= ~(1 << 7)
执行完之后,seg
对应8个bit位存储的值为
0
0
0
0
0
0
0
0
假设当前seg
对应8个bit位存储的值为
1
0
0
0
1
0
0
0
如果要获取index=3位置的标识位,需要使用左移位运算<<
和按位与运算&
来实现
boolean isTrue = (seg & 1 << 3) > 0
1
0
0
0
1
0
0
0
&
0
0
0
0
1
0
0
0
=
0
0
0
0
1
0
0
0
计算结果大于0,说明标识位是1,反之则为0
BitSet内部的原理就是利用位运算来实现的,不过它不是使用byte类型来存储数据,而是用long类型,这样一个元素就能存储64bit,在寻找index的真实bit位存储位置时,需要用index除以64或者使用位运算右移6位,这些BitSet内部都已经封装好了,直接使用即可,需要注意点的是,BitSet不是线程安全的,如果要保证线程安全需要自己加锁处理。