【发布时间】:2020-05-15 08:21:13
【问题描述】:
我正在调查BitSet,但我不清楚以下内容:
- 当我们传递位数时,会除以
6。为什么使用6而不是 2 的幂? - 在初始化底层数组时,为什么在除以
6之前先减 1,然后再加 1?
【问题讨论】:
标签: java performance byte bitset
我正在调查BitSet,但我不清楚以下内容:
6。为什么使用 6 而不是 2 的幂? 6 之前先减 1,然后再加 1?【问题讨论】:
标签: java performance byte bitset
我假设您询问的是 JDK 中的这段代码:
private static int wordIndex(int bitIndex) {
return bitIndex >> ADDRESS_BITS_PER_WORD; // ADDRESS_BITS_PER_WORD is 6, question 1
}
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]; // question 2
}
initWords 初始化 long[] 以支持这些位,本质上是将这些位存储到 64 位的“字”中。 请注意,这似乎是一个实现细节。这个long[] 应该有多长?嗯,它应该是最后一个单词的单词索引 + 1,因为索引是从零开始的。
什么是最后一个单词的索引?好吧,wordIndex 方法可以告诉我们一个 bit 的单词索引,所以如果我们给它最后一位的索引,nbits - 1(同样因为索引是从零开始的),它会给我们想要的。这应该回答你的第二个问题。
wordIndex如何找到单词索引?好吧,long 有 64 位,所以我们只需要将bitIndex 除以 64。除以 64 的另一种方法是什么?左移 6 次,因为 64 = 2 的 6 次方。有关详细信息,请参阅 this post。
【讨论】:
我想你说说这个类的实现吧?
它在源文件的注释中这样说:
/*
* BitSets are packed into arrays of "words." Currently a word is
* a long, which consists of 64 bits, requiring 6 address bits.
* The choice of word size is determined purely by performance concerns.
*/
所以从给定的位数,低 6 位用于寻址 64 位字的位,其余位用于寻址字。
对于第 2 点,我想你在谈论
wordIndex(nbits-1) + 1
这是
bitIndex >> ADDRESS_BITS_PER_WORD
等等。
这意味着,您将原始范围 (1-64, 65-128) 映射到“减一” (0-63, 64-127),除以 64 (0, 1) 并再次增加结果 (1 , 2) 获取数组中需要的单词数。
为了证明两者:
假设您想要一个包含 128 个条目的 BitSet。你初始化它,你会得到一个包含 2 个 64 位条目的数组。为什么?
那是因为 wach word 可以容纳 64 位,所以为了容纳 128 位,您需要 2 个数组条目:
(128-1)/64 + 1 = 127/64 + 1 = 1 + 1 = 2。(请记住,整数除法趋向于较小的值。)
现在,您要设置 bis 5、13 和 66。
第 5 位和第 13 位很好 - 您只需在索引 0 处设置字中的第 5 位和第 13 位。
但是你用 66 做什么呢?每个字只有 64 位! (0..63)
好吧,在这种情况下,您在数组中执行的每一步都减去 64。因此,您转到索引 1 处的单词,对于“补偿”,您从 66 转到 2。
这正是这些位操作所发生的:从这些位索引中的每一个中,低 6 位被取出并用作相应字中的位地址。
5 = 0 000101 = 0/5 13 = 0 001101 = 0/13 66 = 1 000001 = 1/2
所以
【讨论】:
int 参数的构造函数中初始化 long[] hg.openjdk.java.net/jdk7/jdk7/jdk/file/00cd9dc3c2b5/src/share/…