标签:openjdk   源代码   java   bitset   
概要
java.lang.Object
    java.util.BitSet
 
public class BitSet
extends Object
implements Cloneable, Serializable
 
BitSet 类用来支持位操作,给它一个 size ,就会返回一个对象,代表 size 个位。可以完成“与或非”操作。
实现
试想一下,long 最多也就 64 位,假如我们想对 1000 位进行一些运算,要如何实现呢?这个类就告诉我们怎么用一个数组,去实现位操作。
内部使用 long 类型的数组来存储数据。
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];
}
private static int wordIndex(int bitIndex) {
    return bitIndex >> ADDRESS_BITS_PER_WORD;
}
 
初始化会根据的位数决定要申请多大的数组,long 类型是 64 位,所以你如果 nbits 是 1~64,你只需要一个长度为1的数组就好。
要是数组不够用了,就要进行扩充,下面的函数会根据申请的 long 元素个数,经过与当前元素个数2倍的比较进行扩充。
private void ensureCapacity(int wordsRequired) {
    if (words.length < wordsRequired) {
        // Allocate larger of doubled size or required size
        int request = Math.max(2 * words.length, wordsRequired);
        words = Arrays.copyOf(words, request);
        sizeIsSticky = false;
    }
}
 
public void flip(int bitIndex) {
    if (bitIndex < 0)
        throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
    int wordIndex = wordIndex(bitIndex);
    expandTo(wordIndex);
    words[wordIndex] ^= (1L << bitIndex);
    recalculateWordsInUse();
    checkInvariants();
}
 
先根据索引位置 bitIndex 计算出相应的位在数组哪个元素里,然后再将 1 左移 bitIndex 位后与此元素作异或运算。注意bitIndex 如果超过了
 64 位,会又循环回来,比如 1L << 69 其实和 1L
 << 5 是一样的,只不过异或的时候,一个与words[1] 异或,一个与 words[0]。
类中还有其它位操作,比如置1,清0,只是和 flip 的位操作符不同。
还有一类是区间内翻转,这需要首先临到一个相应区间全为1的数字,再与 words 相应元素作运算。
public void flip(int fromIndex, int toIndex) {
    checkRange(fromIndex, toIndex);
    if (fromIndex == toIndex)
        return;
    int startWordIndex = wordIndex(fromIndex);
    int endWordIndex   = wordIndex(toIndex - 1);
    expandTo(endWordIndex);
    long firstWordMask = WORD_MASK << fromIndex;
    long lastWordMask  = WORD_MASK >>> -toIndex;
    if (startWordIndex == endWordIndex) {
        // Case 1: One word
        words[startWordIndex] ^= (firstWordMask & lastWordMask);
    } else {
        // Case 2: Multiple words
        // Handle first word
        words[startWordIndex] ^= firstWordMask;
        // Handle intermediate words, if any
        for (int i = startWordIndex+1; i < endWordIndex; i++)
            words[i] ^= WORD_MASK;
        // Handle last word
        words[endWordIndex] ^= lastWordMask;
    }
    recalculateWordsInUse();
    checkInvariants();
}
 
如果区间跨越多个数组元素,还需要把中间的数个数组元素内容全部翻转。
public void and(BitSet set) {
    if (this == set)
        return;
    while (wordsInUse > set.wordsInUse)
        words[--wordsInUse] = 0;
    // Perform logical AND on words in common
    for (int i = 0; i < wordsInUse; i++)
        words[i] &= set.words[i];
    recalculateWordsInUse();
    checkInvariants();
}
 
从这个函数体会一下,两个 BitSet 对象之间的 AND 操作如何进行,其实就是对应的数组元素之间作 AND 操作就行。
public int hashCode() {
    long h = 1234;
    for (int i = wordsInUse; --i >= 0; )
        h ^= words[i] * (i + 1);
    return (int)((h >> 32) ^ h);
}
 
计算哈希值的操作,说实话,我是不太明白为什么这样算哈希值的,为什么这样能减少不同 BitSet 之间的碰撞呢?
剩下的东西我也不想分析了,总之,需要把握整体的思路,就是如何用一个数组去实现位操作,每次操作需要弄清楚,在数组的哪些元素上操作,与什么数字作位操作,做什么位操作。
OpenJDK 源代码阅读之 BitSet,布布扣,bubuko.com
OpenJDK 源代码阅读之 BitSet
标签:openjdk   源代码   java   bitset   
原文地址:http://blog.csdn.net/on_1y/article/details/29219675