Lucene工具箱之OpenBitSet(一)
3.4?????????Lucene工具箱之OpenBitSet
????
在Lucene中,DocId具有這樣的特征:唯一/遞增。而且在搜索的過程,不同term之間的DocId集合進行邏輯運算的需求非常之多。OpenBitSet正是集合運算的利器。
3.4.1?????????????OpenBitSet的原理
假設有一個byte,一共有8個二進制位,如下圖:
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
如果每個二進制位表示一個數,這個Byte可以存儲[0,7]共8個數。
比如存儲4,6這兩個數,則byte中各個二進制位的狀態如下:
0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
用二進制位的下標表示存儲的數,并在將二進制位的相應狀態設為1。OpentBitSet正是利用上述原理來存儲數據。
3.4.2 OpenBitSet的簡單應用
假設有兩個集合A = {1,3,4,10,5},B={5,3,2,8}。計算A集合與B集合的并集;計算A集合與B集合的交集。
?????????????????? int[] a = {1,3,4,10,5};
?????? int [] b = {5,3,2,8};
?????? OpenBitSet setA = new OpenBitSet();
?????? for(int i : a) setA.set(i);
?????? OpenBitSet setB = new OpenBitSet();
?????? for(int i : b) setB.set(i);
? ? ??
?????? OpenBitSet unionSet = setA.clone();
?????? unionSet. union(setB); //取A與B的并集
?????? DocIdSetIterator iterator = unionSet.iterator();
?????? while(iterator.nextDoc()!=DocIdSetIterator.NO_MORE_DOCS){
?????????? System.out.print(iterator.docID()+", ");
?????? }System.out.println();
? ? ??
?????? OpenBitSet intersectionSet = setA.clone();
?????? intersectionSet. intersect(setB);//取A與B的交集
?????? iterator = intersectionSet.iterator();
?????? while(iterator.nextDoc()!=DocIdSetIterator.NO_MORE_DOCS){
?????????? System.out.print(iterator.docID()+", ");
?????? }System.out.println();
輸出結果如下:
??? 并集:1, 2, 3, 4, 5, 8, 10,
交集:3, 5,
3.4.3 OpenBitSet的源碼分析
????OpenBitSet利用二進制位來存儲數據,一個long類型最高只有64位,能存儲63個數。
????如果存儲[0,63]之間的數,需要1個long類型串聯起來。
如果存儲[0,127]之間的數,需要2個long類型串聯起來。
如果存儲[0,191]之間的數,需要3個long類型串聯起來。
……
如果存儲[0,(64N+m)] (N,m為非負整數,m<64)之間的數,需要N個long類型串聯起來.
所以OpenBitSet的核心就是一個long類型的數組bits。
public class OpenBitSetextendsDocIdSet implements Bits, Cloneable {
? protectedlong[]bits;
這個數組需要開多大呢?依據存儲數據的最大值而定。OpenBitSet有構造函數如下:
? publicOpenBitSet() {
??? this(64);
? }
這個構造函數調用了另一個需要傳參的構造函數:
? /** Constructs an OpenBitSet large enough to hold <code>numBits</code>.
?? */
? publicOpenBitSet(long numBits) {
??? this.numBits = numBits;
??? bits = new long[bits2words(numBits)];
??? wlen= bits.length;
? }
該構造函數中調用了bits2words()方法來通過傳入的參數計算bits數組的大小。
tits2words(64) = 1;表示存儲[0,63]之間的數需要1個long類型。
tits2words(256)=2;表示需要存儲[0,255]之間的數需要2個long類型。
依此類推……
這樣傳參避免我們人工計算bits數組的大小,也封裝了實現原理。
OpenBitSet的數據存儲
首先要清楚的是,在OpenBitSet中:
[0,63]存儲在bits[0]的64個位中
[64,127]存儲在bits[1]的64個位中
……
[64N,64N+63]存儲在bits[N]的64個位中
任何一個非負整數,都可以表示成:64*N+m (N,m都是非負整數,m<64)。其中N表示bits數組的下標,m表示bits[N]的64個位中需要把狀態置為1的二進制位的下標。
存儲數據的原代碼如下:
? /** sets a bit, expanding the set size if necessary */
? publicvoidset(longindex) {
??? int wordNum = expandingWordNum(index);
??? int bit = (int)index & 0x3f;
??? long bitmask = 1L << bit;
??? bits[wordNum] |= bitmask;
? }
整個set方法有4句代碼,我們一句一句分析:
第1句代碼求公式64*N+m中的N。參數index除以64或者 index>>6就可以了。左移6位即除以2^6=64.
第2句代碼求公式64*N+m中的m。注意0x3f= 64 =(111111)2,index%64 即為 index & 0x3f 。
? ??第3句和第4句即把bits[N]的第m位設置為1。
????最后總結一下OpenBitSet數據存儲的特點:OpenBitSet無法存儲重復的數據。數據存儲到OpenBitSet中后就是有序的了。OpenBitSet適合存儲密集程度高,且量大的數據。OpenBitSet中存儲的數據適合位運算,比如取交集、并集、補集……
? ? 由于直接從word中粘貼來受到了長度的限制,我又不想在博客編輯器中重新寫一遍,所以關于OpenBitSetIterator相關的內容和Lucene4.2的其它細節可以從我的《Lucene4.x源碼解讀》第4章4.3節中了解。OpenBitSetIterator分析了bitList的實現原理。
????《Lucene4.x源碼解讀》會不定時更新,可以關注我的新浪微博 @帥廣應s 。
轉載于:https://blog.51cto.com/sbp810050504/1567796
總結
以上是生活随笔為你收集整理的Lucene工具箱之OpenBitSet(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu update case t
- 下一篇: SpringMVC _Controlle