Java BitSet 源碼解析(2)

參考:

java.util.BitSet

Java BitSet類


查看類 ArrayListremoveIf 方法源碼時,發現其使用 BitSet 類來存儲待刪除的元素下標

之前沒有接觸過這個類,瞭解之後發現其在數據查詢和存儲方面有很大用處


主要內容:

  1. BitSet 淺析
  2. 類變量和常量
  3. 構造器
  4. set
  5. clear -(2)
  6. get
  7. flip - (3)
  8. valueOf
  9. 位運算(and, andNot, or, xor
  10. next
  11. previous - (4)
  12. 判空 / 判斷交集
  13. 大小(length, size, cardinality
  14. toByteArraytoLongArray
  15. BitSet 分析
  16. Java 素數查找
  17. 小結

clear

在前面的 set 方法中,如果設置位值爲 false,那麼會調用函數 clear

函數 clear 的功能是設置位值爲 false,類 BitSet 提供了 3 種重載方式:

public void clear(int bitIndex)
public void clear(int fromIndex, int toIndex)
public void clear()
  • clear(int bitIndex)

源碼如下:

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

    int wordIndex = wordIndex(bitIndex);
    if (wordIndex >= wordsInUse)
        return;

    words[wordIndex] &= ~(1L << bitIndex);

    recalculateWordsInUse();
    checkInvariants();
}

輸入參數 bitIndex 表示位集下標

該函數功能是設置位集中下標爲 bitIndex 的位值爲 false

第一步:判斷參數 bitIndex 是否小於 0,如果成立,拋出 IndexOutOfBoundsException 異常

第二步:定義變量 wordIndex 計算數組 words 中相對應的下標;並判斷是否變量 wordIndex 在當前數組已使用個數範圍內,如果不是,直接退出(因爲默認爲 false

第三步:利用位左移運算符和位取反運算符計算掩碼,然後對數組中下標 wordIndex 的值進行位與操作,設置該位值爲 false

第四步:調用函數 recalculateWordsInUse 重新計算當前數組已使用個數;調用函數 checkInvariants 判斷類變量是否符合條件

  • clear(int fromIndex, int toIndex)

源碼如下:

public void clear(int fromIndex, int toIndex) {
    checkRange(fromIndex, toIndex);

    if (fromIndex == toIndex)
        return;

    int startWordIndex = wordIndex(fromIndex);
    if (startWordIndex >= wordsInUse)
        return;

    int endWordIndex = wordIndex(toIndex - 1);
    if (endWordIndex >= wordsInUse) {
        toIndex = length();
        endWordIndex = wordsInUse - 1;
    }

    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] = 0;

        // Handle last word
        words[endWordIndex] &= ~lastWordMask;
    }

    recalculateWordsInUse();
    checkInvariants();
}

該函數用於設置一段範圍內的位值爲 false

Note:該範圍爲 [fromIndex, toIndex),不包括下標 toIndex 的位

使用方式和 set(int fromIndex, int toIndex) 類似

第一步:調用函數 checkRange 判斷輸入參數是否符合條件

第二步:判斷輸入參數 fromIndextoIndex 是否相等,如果相等,直接退出

第三步:計算數組 words 中的起始下標 startWordIndex,如果該下標已超出當前數組 words 中已使用的個數,那麼直接退出

第四步:計算數組 words 中的結束下標 endWordIndex,同樣的,如果該下標已超出當前數組 words 中已使用的個數,那麼調用函數 length() 計算當前已使用位集長度,賦值給 toIndex,同時設置 endWordIndex 值爲數組已使用的最大下標

第五步:計算起始掩碼 firstWordMask 和 結束掩碼 lastWordMask,將其使用於位操作中

第六步:調用函數 recalculateWordsInUse 重新計算當前數組已使用個數;調用函數 checkInvariants 判斷類變量是否符合條件

  • clear()

源碼如下:

public void clear() {
    while (wordsInUse > 0)
        words[--wordsInUse] = 0;
}

該函數功能是設置位集所有位爲 false

recalculateWordsInUse

源碼如下:

private void recalculateWordsInUse() {
    // Traverse the bitset until a used word is found
    int i;
    for (i = wordsInUse-1; i >= 0; i--)
        if (words[i] != 0)
            break;

    wordsInUse = i+1; // The new logical size
}

該函數用於重新計算數組 words 中已使用的個數


get

如果想要獲取位值,可以調用函數 get,類 BitSet 提供了 2 種重載方式:

public boolean get(int bitIndex)
public BitSet get(int fromIndex, int toIndex)
  • get(int bitIndex)

源碼如下:

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

該函數功能是得到位集中對應下標 bitIndex 的位值

第一步:判斷輸入參數是否符合條件,以及調用函數 checkInvariants 判斷類變量是否符合條件(不太明白爲啥要在這裏弄這個

第二步:計算位集下標對應的數組下標 wordIndex,進行邏輯與操作,如果下標 wordIndex 超出數組已使用範圍,那麼直接返回 false;否則,返回該位值

  • get(int fromIndex, int toIndex)

源碼如下:

public BitSet get(int fromIndex, int toIndex) {
    checkRange(fromIndex, toIndex);

    checkInvariants();

    int len = length();

    // If no set bits in range return empty bitset
    if (len <= fromIndex || fromIndex == toIndex)
        return new BitSet(0);

    // An optimization
    if (toIndex > len)
        toIndex = len;

    BitSet result = new BitSet(toIndex - fromIndex);
    int targetWords = wordIndex(toIndex - fromIndex - 1) + 1;
    int sourceIndex = wordIndex(fromIndex);
    boolean wordAligned = ((fromIndex & BIT_INDEX_MASK) == 0);

    // Process all words but the last word
    for (int i = 0; i < targetWords - 1; i++, sourceIndex++)
        result.words[i] = wordAligned ? words[sourceIndex] :
            (words[sourceIndex] >>> fromIndex) |
            (words[sourceIndex+1] << -fromIndex);

    // Process the last word
    long lastWordMask = WORD_MASK >>> -toIndex;
    result.words[targetWords - 1] =
        ((toIndex-1) & BIT_INDEX_MASK) < (fromIndex & BIT_INDEX_MASK)
        ? /* straddles source words */
        ((words[sourceIndex] >>> fromIndex) |
         (words[sourceIndex+1] & lastWordMask) << -fromIndex)
        :
        ((words[sourceIndex] & lastWordMask) >>> fromIndex);

    // Set wordsInUse correctly
    result.wordsInUse = targetWords;
    result.recalculateWordsInUse();
    result.checkInvariants();

    return result;
}

該函數返回一個新的 BitSet 對象,用於保存當前位集中範圍在 [fromIndex, toIndex) 的位值

第一步:調用函數 checkRange 檢查輸入參數是否符合條件,同時調用函數 checkInvariants 判斷類變量是否符合條件

第二步:定義變量 len,計算位集大小,如果當前位集長度不大於起始下標 fromIndex 或者兩個輸入參數相等,那麼創建一個空的 BitSet 對象並返回;如果終止下標大於當前位集大小,則設置終止下標 toIndex 大小爲當前位集大小 len

第三步:創建類 BitSet 對象 result,初始位集大小爲待取值的位集範圍;定義變量 targetWords,賦值爲待取值的位集轉換成的長整型整數個數;定義變量 sourceIndex,計算當前數組 words 中對應的起始下標;定義變量 wordAligned,計算起始下標是否是 64 的整數倍

第四步:前面已經計算得出,待取值的位集可轉換爲 targetWordslong 型整數。先從當前數組 words 中取出前 targetWors-1 個位值,然後再取出剩餘部分的位值

第五步:設置 result 的變量並返回

實例測試

下面輸入具體參數來驗證函數 get(int fromIndex, int toIndex) 的計算過程

首先創建類對象 bitSet,設置位集 [3, 200)true

調用函數 get,輸入參數 fromIndex = 5, toIndex = 147

0 < fromIndex < toIndex,所以符合函數 checkRange 判斷條件

變量 len = 200,經過判斷後,fromIndextoIndex 的值不變

創建類對象 result = new BitSet(142),定義變量 targetWords = wordIndex(147-5-1)+1 = 3,定義變量 sourceIndex = 0,定義變量 wordAligned = false

for 循環如下所示:

for (int i=0; i<2; i++,sourceIndex ++)
    ...

其中

result.words[0] = (words[0] >>> fromIndex) | (words[1] << -fromIndex) 
    = (0xfffffffffffffff8L >>> 00 0101) | (0xffffffffffffffffL << 11 1011) 
    = (words[0] >>> 5) | (words[1] << 59)
    = 0x7fffffffffffffffL | 0xf800000000000000L
    = 0xffffffffffffffffL

result.words[1] = (words[1] >>> fromIndex) | (words[2] << -fromIndex) 
    = (0xffffffffffffffffL >>> 00 0101) | (0xffffffffffffffffL << 11 1011) 
    = (words[0] >>> 5) | (words[1] << 59)
    = 0xffffffffffffffffL

現在處理最後一個 word

定義變量

lastWordMask = WORD_MASK >>> -toIndex = 0xffffffffffffffffL >>> 10 1101 = WORD_MASK >>> 45 = 0x00000000000effffL

(toIndex-1) & BIT_INDEX_MASK) = 146 & 63 = 2^7+2^4+2 & 111111 = 1001 0010 & 0011 1111 = 0001 0010 = 18

fromIndex & BIT_INDEX_MASK = 5 & 63 = 0000 0101 & 0011 1111 = 0000 0101 = 5

所以最後一個值的計算公式爲

((words[sourceIndex] & lastWordMask) >>> fromIndex) = (0xffffffffffffffffL & 0x00000000000effffL) >>> 5
 = 0x00000000000effffL >>> 5 = 0x000000000003fffL

完整代碼如下:

public static void main(String args[]) {
    BitSet bitSet = new BitSet();

    bitSet.set(3, 200);
    System.out.println(bitSet.cardinality());

    BitSet result = bitSet.get(5, 147);
    System.out.println(result.cardinality());

    long[] res = result.toLongArray();
    for (Long l : res) {
        System.out.println(Long.toHexString(l));
    }
}

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章