1. 程式人生 > >roaringbitmap 原始碼解析(3)底層容器相互add過程

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 思路和上面的差不多,總而言之就是。先確定範圍。然後在計算。然後再優化計算。

未完待續。。。