NOTE
BloomFilter
1. Usage 2. Source analysis 3. References
This is a historical learning note and may contain outdated or incomplete understanding.
1. Usage
BloomFilter<Integer> integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), 1024 * 1024 * 32, 0.0000001d);
integerBloomFilter.put(1);
integerBloomFilter.put(2);
integerBloomFilter.put(3);
boolean c4 = integerBloomFilter.mightContain(4);
boolean c3 = integerBloomFilter.mightContain(3);
System.out.println(c4);
System.out.println(c3);
2. Source Analysis
- Key fields
BloomFilter
// bit array
private final BitArray bits;
// number of hash functions
private final int numHashFunctions;
// interface that converts an arbitrary type into Java primitive data
private final Funnel<? super T> funnel;
// interface for operating on the bit array: put and mightContain
private final Strategy strategy;
- Create a BloomFilter
static <T> BloomFilter<T> create(
Funnel<? super T> funnel, int expectedInsertions /* n */, double fpp, Strategy strategy) {
checkNotNull(funnel);
checkArgument(expectedInsertions >= 0, "Expected insertions (%s) must be >= 0",
expectedInsertions);
checkArgument(fpp > 0.0, "False positive probability (%s) must be > 0.0", fpp);
checkArgument(fpp < 1.0, "False positive probability (%s) must be < 1.0", fpp);
checkNotNull(strategy);
if (expectedInsertions == 0) {
expectedInsertions = 1;
}
// Calculate the bit-array length from the expected number of elements and false-positive rate.
long numBits = optimalNumOfBits(expectedInsertions, fpp);
// Calculate the number of hash functions from the expected number of elements and bit-array length.
int numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits);
try {
// Create the BloomFilter from the bit array, hash-function count,
// type-conversion interface, and bit-array operation strategy.
return new BloomFilter<T>(new BitArray(numBits), numHashFunctions, funnel, strategy);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Could not create BloomFilter of " + numBits + " bits", e);
}
}
put
MURMUR128_MITZ_32 is the default.
At an abstract level, put is a write and mightContain is a read. Their code is somewhat similar. Both first use Murmur3 hash to calculate a 128-bit byte array from the input funnel, then take the low and high eight bytes (64 bits each) to create two long hash values, hash1 and hash2. Inside the loop, the two hash values are used to simulate additional hash functions, following the idea mentioned above: gi(x) = h1(x) + i h2(x). In other words, hash2 is accumulated each time, and the result is indexed into the bit array by taking the modulus of bitSize.
public <T> boolean put(T object, Funnel<? super T> funnel, int numHashFunctions, BloomFilterStrategies.BitArray bits) {
long bitSize = bits.bitSize();
byte[] bytes = Hashing.murmur3_128().hashObject(object, funnel).getBytesInternal();
long hash1 = this.lowerEight(bytes);
long hash2 = this.upperEight(bytes);
boolean bitsChanged = false;
long combinedHash = hash1;
for(int i = 0; i < numHashFunctions; ++i) {
bitsChanged |= bits.set((combinedHash & 9223372036854775807L) % bitSize);
combinedHash += hash2;
}
return bitsChanged;
}
mightContain
MURMUR128_MITZ_32 is the default.
public <T> boolean mightContain(T object, Funnel<? super T> funnel, int numHashFunctions, BloomFilterStrategies.BitArray bits) {
long bitSize = bits.bitSize();
byte[] bytes = Hashing.murmur3_128().hashObject(object, funnel).getBytesInternal();
long hash1 = this.lowerEight(bytes);
long hash2 = this.upperEight(bytes);
long combinedHash = hash1;
for(int i = 0; i < numHashFunctions; ++i) {
if (!bits.get((combinedHash & 9223372036854775807L) % bitSize)) {
return false;
}
combinedHash += hash2;
}
return true;
}
2.1. Summary
Set S contains n elements. These n elements are mapped by k hash functions into bit array B of length m. The values of m and k depend on the false-positive probability fpp and the total number of elements n.
static <T> BloomFilter<T> create(Funnel<? super T> funnel, int expectedInsertions /* n */, double fpp, Strategy strategy)
-
Input data
funnel- Used to calculate a 128-bit array.
-
Expected total number of inserted elements
expectedInsertions, and expected false-positive probabilityfpp. -
Strategy- Has
putandmightContainmethods.
- Has
-
put- Calculate a 128-bit array from
funnel, split its high and low 64 bits into two hash values, and use those two values to derive additional hash functions. - Use those hash functions to calculate element hash positions, take the modulus by the bit-array length, and set those positions to 1.
- Calculate a 128-bit array from
-
mightContain- Same basic process as
put. - Iterate over every hash function and check whether all corresponding positions are 1; if so, the element may exist.
- Same basic process as
Discussion
Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub