A Bloom filter has one quirk that sets it apart from an exact set: it can say “yes” when the answer is actually “no”. This is called a false positive.
When you insert items, each one flips several bits to 1. Over time, the bit array fills up. Eventually, a new item that was never inserted might have all its hash positions land on bits that were already flipped by other items. The filter sees all 1s and reports “possibly present” — even though the item was never added.
Here is a small example. Suppose the filter has 8 bits and uses 2 hash functions.
After inserting "apple" (positions 1, 5):
0 1 0 0 0 1 0 0
After inserting "grape" (positions 3, 5):
0 1 0 1 0 1 0 0
Testing "mango" (positions 1, 3):
Both bits are 1 → filter says "possibly present"
But "mango" was never inserted!
The bits at positions 1 and 3 were set by "apple" and "grape". They just happened to match "mango"’s hash positions.
A Bloom filter can never say “no” when the answer is “yes”. That is its strongest guarantee.
When you insert an item, you set its bits to 1. Those bits stay 1 forever — a Bloom filter never clears bits. So when you test the same item later, all its positions are still 1, and the filter correctly says “possibly present”.
The only way to get a false negative would be if a bit got flipped back to 0 after insertion. Since that never happens, false negatives are structurally impossible.
This asymmetry makes Bloom filters useful in specific scenarios. Whenever you can tolerate some false alarms but cannot afford to miss real matches, a Bloom filter fits well.
For example, a password strength checker might use a Bloom filter to screen against known weak passwords. A false positive means a strong password gets flagged as weak — slightly annoying, but harmless. A false negative would mean a known weak password gets accepted — a real problem.