public static BloomKFilter build(long maxNumEntries) {
    checkArgument(maxNumEntries > 0, "expectedEntries should be > 0");
    long numBits = optimalNumOfBits(maxNumEntries, DEFAULT_FPP);
    int k = optimalNumOfHashFunctions(maxNumEntries, numBits);
    long nLongs = (long) Math.ceil((double) numBits / (double) Long.SIZE);
    // additional bits to pad long array to block size
    long padLongs = DEFAULT_BLOCK_SIZE - nLongs % DEFAULT_BLOCK_SIZE;
    long m = (nLongs + padLongs) * Long.SIZE;
    long[] marr = new long[(int) Math.ceil((double) m / (double) Long.SIZE)];
    checkArgument((marr.length % DEFAULT_BLOCK_SIZE) == 0, "bitSet has to be block aligned");
    int totalBlockCount = marr.length / DEFAULT_BLOCK_SIZE;
    return new BloomKFilter(k, m, totalBlockCount, marr);
  }
BloomKFilter is variation of {@link BloomFilter}. Unlike BloomFilter, BloomKFilter will spread
'k' hash bits within same cache line for better L1 cache performance. The way it works is,
First hash code is computed from key which is used to locate the block offset (n-longs in bitset constitute a block)
Subsequent 'k' hash codes are used to spread hash bits within the block. By default block size is chosen as 8,
which is to match cache line size (8 longs = 64 bytes = cache line size).
Refer {@link BloomKFilter#addBytes(byte[])} for more info.

This implementation has much lesser L1 data cache misses than {@link BloomFilter}.
storage-api/src/java/org/apache/hive/common/util/BloomKFilter.java