Skip to content

Commit 134b42c

Browse files
authored
refactor: BloomFilter (#5325)
1 parent 777de1d commit 134b42c

File tree

2 files changed

+106
-20
lines changed

2 files changed

+106
-20
lines changed

src/main/java/com/thealgorithms/datastructures/bloomfilter/BloomFilter.java

+65-17
Original file line numberDiff line numberDiff line change
@@ -2,60 +2,108 @@
22

33
import java.util.BitSet;
44

5+
/**
6+
* A generic BloomFilter implementation for probabilistic membership checking.
7+
*
8+
* @param <T> The type of elements to be stored in the Bloom filter.
9+
*/
510
public class BloomFilter<T> {
611

7-
private int numberOfHashFunctions;
8-
private BitSet bitArray;
9-
private Hash<T>[] hashFunctions;
12+
private final int numberOfHashFunctions;
13+
private final BitSet bitArray;
14+
private final Hash<T>[] hashFunctions;
1015

11-
public BloomFilter(int numberOfHashFunctions, int n) {
16+
/**
17+
* Constructs a BloomFilter with a specified number of hash functions and bit array size.
18+
*
19+
* @param numberOfHashFunctions the number of hash functions to use
20+
* @param bitArraySize the size of the bit array
21+
*/
22+
@SuppressWarnings("unchecked")
23+
public BloomFilter(int numberOfHashFunctions, int bitArraySize) {
1224
this.numberOfHashFunctions = numberOfHashFunctions;
13-
hashFunctions = new Hash[numberOfHashFunctions];
14-
bitArray = new BitSet(n);
15-
insertHash();
25+
this.bitArray = new BitSet(bitArraySize);
26+
this.hashFunctions = new Hash[numberOfHashFunctions];
27+
initializeHashFunctions();
1628
}
1729

18-
private void insertHash() {
30+
/**
31+
* Initializes the hash functions with unique indices.
32+
*/
33+
private void initializeHashFunctions() {
1934
for (int i = 0; i < numberOfHashFunctions; i++) {
20-
hashFunctions[i] = new Hash(i);
35+
hashFunctions[i] = new Hash<>(i);
2136
}
2237
}
2338

39+
/**
40+
* Inserts an element into the Bloom filter.
41+
*
42+
* @param key the element to insert
43+
*/
2444
public void insert(T key) {
2545
for (Hash<T> hash : hashFunctions) {
26-
int position = hash.compute(key) % bitArray.size();
46+
int position = Math.abs(hash.compute(key) % bitArray.size());
2747
bitArray.set(position);
2848
}
2949
}
3050

51+
/**
52+
* Checks if an element might be in the Bloom filter.
53+
*
54+
* @param key the element to check
55+
* @return {@code true} if the element might be in the Bloom filter, {@code false} if it is definitely not
56+
*/
3157
public boolean contains(T key) {
3258
for (Hash<T> hash : hashFunctions) {
33-
int position = hash.compute(key) % bitArray.size();
59+
int position = Math.abs(hash.compute(key) % bitArray.size());
3460
if (!bitArray.get(position)) {
3561
return false;
3662
}
3763
}
3864
return true;
3965
}
4066

41-
private class Hash<T> {
67+
/**
68+
* Inner class representing a hash function used by the Bloom filter.
69+
*
70+
* @param <T> The type of elements to be hashed.
71+
*/
72+
private static class Hash<T> {
4273

43-
int index;
74+
private final int index;
4475

76+
/**
77+
* Constructs a Hash function with a specified index.
78+
*
79+
* @param index the index of this hash function
80+
*/
4581
Hash(int index) {
4682
this.index = index;
4783
}
4884

85+
/**
86+
* Computes the hash of the given key.
87+
*
88+
* @param key the element to hash
89+
* @return the hash value
90+
*/
4991
public int compute(T key) {
5092
return index * asciiString(String.valueOf(key));
5193
}
5294

95+
/**
96+
* Computes the ASCII value sum of the characters in a string.
97+
*
98+
* @param word the string to compute
99+
* @return the sum of ASCII values of the characters
100+
*/
53101
private int asciiString(String word) {
54-
int number = 0;
55-
for (int i = 0; i < word.length(); i++) {
56-
number += word.charAt(i);
102+
int sum = 0;
103+
for (char c : word.toCharArray()) {
104+
sum += c;
57105
}
58-
return number;
106+
return sum;
59107
}
60108
}
61109
}
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
package com.thealgorithms.datastructures.bloomfilter;
22

33
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.BeforeEach;
45
import org.junit.jupiter.api.Test;
56

67
public class BloomFilterTest {
8+
private BloomFilter<String> bloomFilter;
9+
10+
@BeforeEach
11+
void setUp() {
12+
bloomFilter = new BloomFilter<>(3, 100);
13+
}
714

815
@Test
9-
public void test1() {
16+
public void testIntegerContains() {
1017
BloomFilter<Integer> bloomFilter = new BloomFilter<>(3, 10);
1118
bloomFilter.insert(3);
1219
bloomFilter.insert(17);
@@ -16,12 +23,43 @@ public void test1() {
1623
}
1724

1825
@Test
19-
public void test2() {
20-
BloomFilter<String> bloomFilter = new BloomFilter<>(4, 20);
26+
public void testStringContains() {
2127
bloomFilter.insert("omar");
2228
bloomFilter.insert("mahamid");
2329

2430
Assertions.assertTrue(bloomFilter.contains("omar"));
2531
Assertions.assertTrue(bloomFilter.contains("mahamid"));
2632
}
33+
34+
@Test
35+
void testInsertAndContains() {
36+
bloomFilter.insert("hello");
37+
bloomFilter.insert("world");
38+
39+
Assertions.assertTrue(bloomFilter.contains("hello"));
40+
Assertions.assertTrue(bloomFilter.contains("world"));
41+
Assertions.assertFalse(bloomFilter.contains("java"));
42+
}
43+
44+
@Test
45+
void testFalsePositive() {
46+
bloomFilter.insert("apple");
47+
bloomFilter.insert("banana");
48+
49+
Assertions.assertFalse(bloomFilter.contains("grape"));
50+
Assertions.assertFalse(bloomFilter.contains("orange"));
51+
}
52+
53+
@Test
54+
void testMultipleInsertions() {
55+
for (int i = 0; i < 100; i++) {
56+
bloomFilter.insert("key" + i);
57+
}
58+
59+
for (int i = 0; i < 100; i++) {
60+
Assertions.assertTrue(bloomFilter.contains("key" + i));
61+
}
62+
63+
Assertions.assertFalse(bloomFilter.contains("key" + 200));
64+
}
2765
}

0 commit comments

Comments
 (0)