roaringbitmap 原始碼解析(3)底層容器相互add過程
今天主要講述roaringbitmap的add計算。
先判斷高位是否存在相同的,如果相同,再低位的容器相add。否則,直接跳過
public static RoaringBitmap and(final RoaringBitmap x1, final RoaringBitmap x2) {
final RoaringBitmap answer = new RoaringBitmap();
final int length1 = x1.highLowContainer.size(), length2 = x2.highLowContainer.size();
int pos1 = 0 , pos2 = 0;
while (pos1 < length1 && pos2 < length2) {
final short s1 = x1.highLowContainer.getKeyAtIndex(pos1);
final short s2 = x2.highLowContainer.getKeyAtIndex(pos2);
if (s1 == s2) { //相同則求交集,
final Container c1 = x1.highLowContainer.getContainerAtIndex(pos1);
final Container c2 = x2.highLowContainer.getContainerAtIndex(pos2);
final Container c = c1.and(c2);
if (c.getCardinality() > 0) {
answer.highLowContainer.append(s1, c);
}
++pos1;
++pos2;
} else if (Util.compareUnsigned(s1, s2) < 0) { // s1 < s2 //不同直接移位pos1到 接近相同的位置
pos1 = x1.highLowContainer.advanceUntil(s2, pos1);
} else { // s1 > s2
pos2 = x2.highLowContainer.advanceUntil(s1, pos2);
}
}
return answer;
}
advanceUntil的主要作用是找到下一個位置。
所以今天主要是正對bitmapContainer 和 arrayContainer 以及runContainer之間的add計算。
幾個基本準則。
add滿足交換律:也就是 runContainer.add(runContainer) = runContainer.add(runContainer);
add滿足最小準則:。 bitmapContainer.add(arrayContainer) .結果一定是arrayContainer能放下。
幾個加法
1)首先arrayContainer.add(arrayContainer)
@Override
public ArrayContainer and(final ArrayContainer value2) {
ArrayContainer value1 = this;
final int desiredCapacity = Math.min(value1.getCardinality(), value2.getCardinality());
ArrayContainer answer = new ArrayContainer(desiredCapacity);
answer.cardinality = Util.unsignedIntersect2by2(value1.content, value1.getCardinality(),
value2.content, value2.getCardinality(), answer.content);
return answer;
}
這裡主要是求出最小能使用到空間,然後求交集。求交集unsignedIntersect2by2 這個做了一些優化。
如果兩者直接的差距大於25倍(以前的程式碼好像是64,所以這個值不值得是統計結果還是測試結果。)採用小陣列每次移動一位。
然後跳躍性移動大陣列。
否則採用兩者都只是移動到下一位,然後進行判斷。
@Override
public ArrayContainer and(final ArrayContainer value2) {
ArrayContainer value1 = this;
final int desiredCapacity = Math.min(value1.getCardinality(), value2.getCardinality());
ArrayContainer answer = new ArrayContainer(desiredCapacity);
answer.cardinality = Util.unsignedIntersect2by2(value1.content, value1.getCardinality(),
value2.content, value2.getCardinality(), answer.content);
return answer;
}
第二 bitmap + bitmap
首先估計一下and操作後的長度,如果大於4096 則用bitmap 這個計算簡單,否則用Array
@Override
public Container and(final BitmapContainer value2) {
int newCardinality = 0;
for (int k = 0; k < this.bitmap.length; ++k) {
newCardinality += Long.bitCount(this.bitmap[k] & value2.bitmap[k]);
}
if (newCardinality > ArrayContainer.DEFAULT_MAX_SIZE) {
final BitmapContainer answer = new BitmapContainer();
for (int k = 0; k < answer.bitmap.length; ++k) {
answer.bitmap[k] = this.bitmap[k] & value2.bitmap[k];
}
answer.cardinality = newCardinality;
return answer;
}
ArrayContainer ac = new ArrayContainer(newCardinality);
Util.fillArrayAND(ac.content, this.bitmap, value2.bitmap);
ac.cardinality = newCardinality;
return ac;
}
第三 bitmap+array
因為array and bitmap = bitmap and array。所以實現程式碼放在bitmap裡面
@Override
public ArrayContainer and(final ArrayContainer value2) {
final ArrayContainer answer = new ArrayContainer(value2.content.length);
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
short v = value2.content[k];
answer.content[answer.cardinality] = v;
answer.cardinality += this.bitValue(v);
}
return answer;
}
這個程式碼好像缺了點什麼。應該是迴圈內部加一個判斷才對否則就只直接返回array了
for (int k = 0; k < c; ++k) {
short v = value2.content[k];
if(this.contains(v)) {
answer.content[answer.cardinality] = v;
answer.cardinality += this.bitValue(v);
}
}
嚇得我感覺去git 提交的一個issue.也不知道會有什麼結果。
沒想到還真給我回復了,可惜我錯了,裡面用來一個bitValue。如果這個bitValue==1 則移位到一個坑中。否則即使你放任何值
都會被後面的值覆蓋。
這個比較狠。輕鬆找到是否存在。
protected long bitValue(final short i) {
final int x = Util.toIntUnsigned(i);
return (bitmap[x / 64] >>> x ) & 1;
}
run+array 和run+bitmap 思路和上面的差不多,總而言之就是。先確定範圍。然後在計算。然後再優化計算。
未完待續。。。