BitMap
介绍
- BitMap就是用一个bit位来标记某个元素对应的value,而key就是该元素。
- 举个例子,假设我们要对0-7内的5个元素(4,7,2,5,3)排序(这里假设这些元素没有重复)。
- 那么我们就可以采用Bit-map的方法来达到排序的目的。要表示8个数,我们就只需要8个Bit(1Bytes),首先我们开辟1Byte的空间,将这些空间的所有Bit位都置为0;
- 然后遍历这5个元素,首先第一个元素是4,那么就把4对应的位置为1(可以这样操作 p+(i/8)|(0×01<<(i%8)) 当然了这里的操作涉及到Big-ending和Little-ending的情况,这里默认为Big-ending),因为是从零开始的,所以要把第五位置为1。
- 然后再处理第二个元素7,将第八位置为1,接着再处理第三个元素,一直到最后处理完所有的元素,将相应的位置为1。
- 然后我们现在遍历一遍Bit区域,将该位是一的位的编号输出(2,3,4,5,7),这样就达到了排序的目的。
- BitMap就是利用这种思想处理大量数据的排序、查询和去重。
代码实现
package com.jack.newTech;
import com.google.common.hash.Funnels;
import com.google.common.hash.Hashing;
import com.google.common.math.LongMath;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.sun.istack.internal.Nullable;
import java.math.RoundingMode;
import java.nio.charset.Charset;
import java.util.Arrays;
import static com.google.common.base.Preconditions.checkArgument;
/**
* 仿造Guava BloomFilter实现的只支持String类型的BitMap
* 位图算法
* Created by zhang_j on 2020/7/14
*/
public class BitMap {
//预估元素数量
private int numberAppros;
//可接受的误差率
private double fpp;
//自动计算的最有哈希函数个数
private int numHashFunctions;
//自动计算的最有BitMap长度
private int bitmapLength;
//二进制向量组
private final BitArray bits;
public BitMap(int numbers, double fpp){
this.numberAppros = numbers;
this.fpp = fpp;
bitmapLength = (int) (-numbers * Math.log(fpp) / (Math.log(2) * Math.log(2)));
numHashFunctions = Math.max(1, (int) Math.round((double) bitmapLength / numbers * Math.log(2)));
bits = new BitArray(bitmapLength);
}
public boolean mightContain(String element){
long bitSize = bits.bitSize();
/**
* 一致性hash算法,生成128位key
*/
byte[] bytes = Hashing.murmur3_128().hashObject(element, Funnels.stringFunnel(Charset.forName("UTF-8"))).asBytes();
long hash1 = Longs.fromBytes(
bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]
);
long hash2 = Longs.fromBytes(
bytes[15], bytes[14], bytes[13], bytes[12], bytes[11], bytes[10], bytes[9], bytes[8]
);
long combinedHash = hash1;
for (int i = 0; i < numHashFunctions; i++) {
if(!bits.get((combinedHash & Long.MAX_VALUE) % bitSize)){
return false;
}
combinedHash += hash2;
}
return true;
}
public boolean insert(String element){
long bitSize = bits.bitSize();
/**
* 一致性hash算法,生成128位key
*/
byte[] bytes = Hashing.murmur3_128().hashObject(element, Funnels.stringFunnel(Charset.forName("UTF-8"))).asBytes();
long hash1 = Longs.fromBytes(
bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]
);
long hash2 = Longs.fromBytes(
bytes[15], bytes[14], bytes[13], bytes[12], bytes[11], bytes[10], bytes[9], bytes[8]
);
boolean bitsChanged = false;
long combinedHash = hash1;
for (int i = 0; i < numHashFunctions; i++) {
// Make the combined hash positive and indexable
bitsChanged |= bits.set((combinedHash & Long.MAX_VALUE) % bitSize);
combinedHash += hash2;
}
return bitsChanged;
}
static final class BitArray {
final long[] data;
long bitCount;
BitArray(long bits) {
this(new long[Ints.checkedCast(LongMath.divide(bits, 64, RoundingMode.CEILING))]);
}
// Used by serialization
BitArray(long[] data) {
checkArgument(data.length > 0, "data length is zero!");
this.data = data;
long bitCount = 0;
for (long value : data) {
bitCount += Long.bitCount(value);
}
this.bitCount = bitCount;
}
boolean set(long index) {
if (!get(index)) {
data[(int) (index >>> 6)] |= (1L << index);
bitCount++;
return true;
}
return false;
}
boolean get(long index) {
return (data[(int) (index >>> 6)] & (1L << index)) != 0;
}
long bitSize() {
return (long) data.length * Long.SIZE;
}
long bitCount() {
return bitCount;
}
void putAll(BitArray array) {
checkArgument(
data.length == array.data.length,
"BitArrays must be of equal length (%s != %s)",
data.length,
array.data.length);
bitCount = 0;
for (int i = 0; i < data.length; i++) {
data[i] |= array.data[i];
bitCount += Long.bitCount(data[i]);
}
}
@Override
public boolean equals(@Nullable Object o) {
if (o instanceof BitArray) {
BitArray bitArray = (BitArray) o;
return Arrays.equals(data, bitArray.data);
}
return false;
}
@Override
public int hashCode() {
return Arrays.hashCode(data);
}
}
}
public static void main(String[] args) {
BitMap bitMap = new BitMap(1000000, 0.001);
bitMap.insert("test");
bitMap.insert("test1");
bitMap.insert("test2");
bitMap.insert("test3");
System.out.println(bitMap.mightContain("this"));
System.out.println(bitMap.mightContain("test"));
System.out.println(bitMap.mightContain("test1"));
System.out.println(bitMap.mightContain("test2"));
System.out.println(bitMap.mightContain("test3"));
System.out.println(bitMap.mightContain("test4"));
}