參考:
查看類 ArrayList
中 removeIf
方法源碼時,發現其使用 BitSet
類來存儲待刪除的元素下標
之前沒有接觸過這個類,瞭解之後發現其在數據查詢和存儲方面有很大用處
主要內容:
BitSet
淺析- 類變量和常量
- 構造器
set
clear
-(2)get
flip
- (3)valueOf
- 位運算(
and, andNot, or, xor
) next
previous
- (4)- 判空 / 判斷交集
- 大小(
length, size, cardinality
) toByteArray
和toLongArray
BitSet
分析Java
素數查找- 小結
clear
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
判斷輸入參數是否符合條件
第二步:判斷輸入參數 fromIndex
和 toIndex
是否相等,如果相等,直接退出
第三步:計算數組 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
如果想要獲取位值,可以調用函數 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
的整數倍
第四步:前面已經計算得出,待取值的位集可轉換爲 targetWords
個 long
型整數。先從當前數組 words
中取出前 targetWors-1
個位值,然後再取出剩餘部分的位值
第五步:設置 result
的變量並返回
實例測試
下面輸入具體參數來驗證函數 get(int fromIndex, int toIndex)
的計算過程
首先創建類對象 bitSet
,設置位集 [3, 200)
爲 true
調用函數 get
,輸入參數 fromIndex = 5, toIndex = 147
0 < fromIndex < toIndex
,所以符合函數 checkRange
判斷條件
變量 len = 200
,經過判斷後,fromIndex
和 toIndex
的值不變
創建類對象 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));
}
}