【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://blog.csdn.net/m0_69908381/article/details/130457413
出自【进步*于辰的博客】
参考类:Integer。
文章目录
- 数据结构:位图
- 1、概述
- 2、构造方法摘要
- 3、方法摘要
- 3.1 void and(BitSet set)
- 3.2 void andNot(BitSet set)
- 3.3 int cardinality()
- 3.4 void clear()
- 3.5 void clear(int bitIndex)
- 3.6 void clear(int fromIndex, int toIndex)
- 3.7 Object clone()
- 3.8 boolean equals(Object obj)
- 3.9 void flip(int bitIndex)
- 3.10 void flip(int fromIndex, int toIndex)
- 3.11 boolean get(int bitIndex)
- 3.12 BitSet get(int fromIndex, int toIndex)
- 3.13 int hashCode()
- 3.14 boolean intersects(BitSet set)
- 3.15 boolean isEmpty()
- 3.16 int length()
- 3.17 int nextClearBit(int fromIndex)
- 3.18 int nextSetBit(int fromIndex)
- 3.19 void or(BitSet set)
- 3.20 void set(int bitIndex)
- 3.21 void set(int bitIndex, boolean value)
- 3.22 void set(int fromIndex, int toIndex)
- 3.23 void set(int fromIndex, int toIndex, boolean value)
- 3.24 int size()
- 3.25 String toString()
- 3.26 void xor(BitSet set)
- 4、方法摘要
- 最后
数据结构:位图
启发博文:《Java 中 BitSet》(转发)。
此类是Java中“位图”数据结构的体现,主要用于减少空间的占用。
有点抽象,我引用一张启发博文中的简图:
这个“long数组”指的就是words
。
我通过我的理解为大家画一张更细致的草图:
在下文【属性说明】中可以看到,words
的类型是long[]
,每个元素由8个字节表示,计64位,每一位对应一个数字的“存在状态”(0/1
),数字从0
开始依次对应。
上图中,为何“对应数字”是“倒序”的?因为灰色框内是每个元素(64位)的二进制补码,在设置(将某一位修改为1
)时,底层逻辑是:(以第3.20项为例)
words[wordIndex] |= (1L << bitIndex);
因此,上图示例中words
存储的是64
和65
的“存在状态”,words
是[0, 3]
。
举个栗子:
BitSet set = new BitSet();
set.set(64);
set.set(65);
sout set;// {64, 65}
怎么是[64, 65]
?我再获取一下words
。
Field f1 = BitSet.class.getDeclaredField("words");
f1.setAccessible(true);
sout Arrays.toString((long[]) f1.get(set));// [0, 3]
所以,将[0, 3]
变为[64, 65]
,归功于toString()
(见第3.25项),源码我暂未解析,其底层逻辑无非是“遍历计数”。
1、概述
继承关系:
- java.lang.Object
- java.util.BitSet
所有已实现的接口:
Serializable, Cloneable
public class BitSet extends Object implements Cloneable, Serializable
此类实现了一个按需增长的位向量。位 set
的每个组件都有一个 boolean 值。用非负的整数将 BitSet 的位编入索引。可以对每个编入索引的位进行测试、设置或者清除。通过 逻辑与、逻辑或 和 逻辑异或 操作,可以使用一个 BitSet 修改另一个 BitSet 的内容。
默认情况下,set
中所有位的初始值都是 false
。
每个位 set
都有一个当前大小,也就是该位 set
当前所用空间的位数。注意,这个大小与位 set
的实现有关,所以它可能随实现的不同而更改。位 set
的长度与位 set
的逻辑长度有关,并且是与实现无关而定义的。
除非另行说明,否则将 null
参数传递给 BitSet 中的任何方法都将导致 NullPointerException。 在没有外部同步的情况下,多个线程操作一个 BitSet 是不安全的。
从以下版本开始:
JDK1.0
另请参见:
序列化表格
2、构造方法摘要
属性说明:
private long[] words;
// 表示“长度”是否是用户指定的,暂不知何用
private transient boolean sizeIsSticky = false
// 已使用“单词”的个数(逻辑长度)
private transient int wordsInUse = 0;
字段说明:
private final static int ADDRESS_BITS_PER_WORD = 6;
private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;// 64
private final static int BIT_INDEX_MASK = BITS_PER_WORD - 1;// 63,暂不知何用
private static final long WORD_MASK = 0xffffffffffffffffL;// -1L,暂不知何用
2.1 null
创建一个新的位 set
。
public BitSet() {
initWords(BITS_PER_WORD);// 初始化,见第4.1项
sizeIsSticky = false;
}
构造出的words
是[0]
。
2.2 int nbits
创建一个位 set
,它的初始大小足以显式表示索引范围在 0
到 nbits - 1
的位。
public BitSet(int nbits) {
if (nbits < 0)
throw new NegativeArraySizeException("nbits < 0: " + nbits);
initWords(nbits);
sizeIsSticky = true;
}
3、方法摘要
3.1 void and(BitSet set)
对此目标位 set
和参数位 set
执行逻辑与操作。
3.2 void andNot(BitSet set)
清除此 BitSet 中所有的位,其相应的位在指定的 BitSet 中已设置。
3.3 int cardinality()
返回此 BitSet 中设置为 true
的位数。
public int cardinality() {
int sum = 0;
for (int i = 0; i < wordsInUse; i++)
sum += Long.bitCount(words[i]);
return sum;
}
bitCount()
的作用是返回二进制补码表示形式的 1
位的数量,可参考Integer类的第4.1项。
3.4 void clear()
将此 BitSet 中的所有位设置为 false
。
public void clear() {
while (wordsInUse > 0)
words[--wordsInUse] = 0;
}
3.5 void clear(int bitIndex)
将索引指定处的位设置为 false
。
3.6 void clear(int fromIndex, int toIndex)
将指定的 fromIndex
(包括)到指定的 toIndex
(不包括)范围内的位设置为 false。
3.7 Object clone()
克隆此 BitSet,生成一个与之相等的新 BitSet。
3.8 boolean equals(Object obj)
将此对象与指定的对象进行比较。
3.9 void flip(int bitIndex)
将指定索引处的位设置为其当前值的补码。
3.10 void flip(int fromIndex, int toIndex)
将指定的 fromIndex
(包括)到指定的 toIndex
(不包括)范围内的每个位设置为其当前值的补码。
3.11 boolean get(int bitIndex)
返回指定索引处的位值。
3.12 BitSet get(int fromIndex, int toIndex)
返回一个新的 BitSet,它由此 BitSet 中从 fromIndex
(包括)到 toIndex
(不包括)范围内的位组成。
3.13 int hashCode()
返回此位 set
的哈希码值。
3.14 boolean intersects(BitSet set)
如果指定的 BitSet 中有设置为 true
的位,并且在此 BitSet 中也将其设置为 true
,则返回 true
。
3.15 boolean isEmpty()
如果此 BitSet 中没有包含任何设置为 true
的位,则返回 ture。
3.16 int length()
返回此 BitSet 的“逻辑大小”:BitSet 中最高设置位的索引加 1。
public int length() {
if (wordsInUse == 0)
return 0;
return BITS_PER_WORD * (wordsInUse - 1) +
(BITS_PER_WORD - Long.numberOfLeadingZeros(words[wordsInUse - 1]));
}
numberOfLeadingZeros()
的作用是返回二进制补码表示形式中最高位的 1
位之前0
位的个数,可参考Integer类的第4.15项。
示例:
BitSet set = new BitSet();
set.set(64);
set.set(65);
sout set;// {64, 65}
Field f1 = BitSet.class.getDeclaredField("wordsInUse");
f1.setAccessible(true);
sout f1.get(set);// 2
sout set.length();// 66
3.17 int nextClearBit(int fromIndex)
返回第一个设置为 false
的位的索引,这发生在指定的起始索引或之后的索引上。
示例:
BitSet set = new BitSet();
set.set(64);
set.set(65);
sout set;// {64, 65}
sout set.nextClearBit(63);// 63
sout set.nextClearBit(64);// 66
3.18 int nextSetBit(int fromIndex)
返回第一个设置为 true
的位的索引,这发生在指定的起始索引或之后的索引上。
示例:
BitSet set = new BitSet();
set.set(64);
set.set(65);
sout set;// {64, 65}
sout set.nextSetBit(63);// 64
sout set.nextSetBit(64);// 64
sout set.nextSetBit(66);// -1
3.19 void or(BitSet set)
对此位 set
和位 set
参数执行逻辑或操作。
3.20 void set(int bitIndex)
将指定索引处的位设置为 true
。
public void set(int bitIndex) {
if (bitIndex < 0)
throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
int wordIndex = wordIndex(bitIndex);
expandTo(wordIndex);// 见第4.3项
words[wordIndex] |= (1L << bitIndex);// ------A
checkInvariants();// 后续解析
}
注意:A 处涉及一个位运算规则,见博文《二进制相关概念、运算与应用》中的【扩展】一栏。
3.21 void set(int bitIndex, boolean value)
将指定索引处的位设置为指定的值。
public void set(int bitIndex, boolean value) {
if (value)
set(bitIndex);// 见上 1项
else
clear(bitIndex);// 见第5项
}
3.22 void set(int fromIndex, int toIndex)
将指定的 fromIndex
(包括)到指定的 toIndex
(不包括)范围内的位设置为 true。
3.23 void set(int fromIndex, int toIndex, boolean value)
将指定的 fromIndex
(包括)到指定的 toIndex
(不包括)范围内的位设置为指定的值。
public void set(int fromIndex, int toIndex, boolean value) {
if (value)
set(fromIndex, toIndex);// 见上1项
else
clear(fromIndex, toIndex);// 见第6项
}
3.24 int size()
返回此 BitSet 表示位值时实际使用空间的位数。
public int size() {
return words.length * BITS_PER_WORD;
}
3.25 String toString()
返回此位 set
的字符串表示形式。
public String toString() {
checkInvariants();
int numBits = (wordsInUse > 128) ?
cardinality() : wordsInUse * BITS_PER_WORD;
StringBuilder b = new StringBuilder(6*numBits + 2);
b.append('{');
int i = nextSetBit(0);
if (i != -1) {
b.append(i);
while (true) {
if (++i < 0) break;
if ((i = nextSetBit(i)) < 0) break;
int endOfRun = nextClearBit(i);
do { b.append(", ").append(i); }
while (++i != endOfRun);
}
}
b.append('}');
return b.toString();
}
底层逻辑就是“遍历计数”
后续解析。
3.26 void xor(BitSet set)
对此位 set
和位 set
参数执行逻辑异或操作。
4、方法摘要
4.1 private void initWords(int nbits)
初始化。
private void initWords(int nbits) {
words = new long[wordIndex(nbits-1) + 1];// 见下 1项
}
之所以+1
,是因为wordIndex()
返回的是“下标”,而+1
对应的是“长度”。
至于为何先-1
,暂未可知。
4.2 private static int wordIndex(int bitIndex)
返回指定数字对应下标。
private static int wordIndex(int bitIndex) {
return bitIndex >> ADDRESS_BITS_PER_WORD;
}
因此,下标是bitIndex >> 6
(words[bitIndex]
),索引是bitIndex % 64
(words[bitIndex]
有64位,bitIndex对应bitIndex % 64
索引)。
4.3 private void expandTo(int wordIndex)
确保BitSet可以容纳给定的wordIndex
,从而暂时违反不变量。调用方必须在返回给用户之前恢复不变量,可能使用recomputeWordsIUse()
。(暂不理解什么是“不变量”)
private void expandTo(int wordIndex) {
int wordsRequired = wordIndex+1;
if (wordsInUse < wordsRequired) {
ensureCapacity(wordsRequired);// 见下 1项
wordsInUse = wordsRequired;
}
}
后续解析。
4.4 private void ensureCapacity(int wordsRequired)
扩容。
private void ensureCapacity(int wordsRequired) {
if (words.length < wordsRequired) {
int request = Math.max(2 * words.length, wordsRequired);
words = Arrays.copyOf(words, request);
sizeIsSticky = false;
}
}
4.5 private void checkInvariants()
变量检验。
private void checkInvariants() {
assert(wordsInUse == 0 || words[wordsInUse - 1] != 0);
assert(wordsInUse >= 0 && wordsInUse <= words.length);
assert(wordsInUse == words.length || words[wordsInUse] == 0);
}
暂且未能完全理解其意图。
最后
如果大家需要Java-API文档,我上传了《Java-API文档-包含5/8/11三个版本》。
本文暂缓更新。