Illustration: 64 bits, 3 hash positions. Cells show 0 or 1; outlines mark the selected key’s positions. A simple browser hash demonstrates the idea; the C# default is Murmur3KirschMitzenmacher.
Definitely absent
One missing bit rules a key out. Skip the unnecessary lookup.
Possibly present
All bits are set? Check your database or original set to be certain.
Only the bits
The filter stores evidence of keys, not the keys themselves.
Build once. Check as you go.
Choose an expected item count and a target false-positive probability. Add keys, then use Contains to decide whether to do an exact lookup.
This example plans for 100,000 keys with a 0.01 target error rate. That is a sizing input, not a measured guarantee.
using M5.BloomFilter;
var filter = FilterBuilder.Build(
expectedElements: 100_000,
errorRate: 0.01);
filter.Add("customer:42");
// true: possibly present
bool maybe = filter.Contains("customer:42");
// false means definitely absent.
// Confirm true results in your source of truth.
Small API. Explicit tradeoffs.
Build & query
FilterBuilder.Build(count, errorRate) creates an IBloomFilter. An overload accepts a HashMethod.
Add, Contains and All accept keys or collections through the provided extensions. Strings use UTF-8; preserve the same encoding and representation when checking keys.
ComputeHash exposes bit positions. Statistics exposes M (bits), K (hash count), N (expected items) and P (calculated ideal false-positive estimate).
Snapshots & concurrency
ToImmutable() returns a detached ReadOnlyFilter snapshot for querying or serialization. Later additions to the original filter do not update the snapshot.
Built-in additions and membership checks synchronize access to filter bits. HashBits returns a detached copy; modifying it does not change the filter.
Async methods return completed tasks around in-memory work; they do not move hashing to a background thread.
What to watch
False positives are allowed. False negatives are not part of the Bloom filter contract when bits, hashing and encoding remain consistent.
Overfilling increases false positives. Size for your workload and rebuild when your data outgrows that plan. There is no delete operation.
A Bloom filter is not an exact set, a security boundary, or a replacement for your original data.
Snapshots that travel.
Use the companion packages for JSON and protobuf. Both carry the bit array and the sizing and hash metadata needed to query the snapshot.
System.Text.Json
Add M5.BloomFilter.Serialization.Json. Register BloomFilterConverter before serializing a ReadOnlyFilter. Its nested bit-array converter is applied automatically.
using System.Text.Json;
using System.Text.Json.Serialization;
using M5.BloomFilter;
var options = new JsonSerializerOptions();
options.Converters.Add(new BloomFilterConverter());
var snapshot = (ReadOnlyFilter)filter.ToImmutable();
var json = JsonSerializer.Serialize(snapshot, options);
var restored = JsonSerializer
.Deserialize<ReadOnlyFilter>(json, options);
Newtonsoft.Json is supported by converters in Newtonsoft.Json. Register its BloomFilterConverter with JsonSerializerSettings; nested bit arrays use their annotated converter.
protobuf-net
Add M5.BloomFilter.Serialization.ProtoBuf. Wrap a filter in the serialization model, then restore it with AsBloomFilter().
using ProtoBuf;
var payload = new M5.BloomFilter
.Serialization.BloomFilter(filter);
using var stream = new MemoryStream();
Serializer.Serialize(stream, payload);
stream.Position = 0;
var decoded = Serializer.Deserialize<
M5.BloomFilter.Serialization.BloomFilter>(stream);
var restored = decoded.AsBloomFilter();
Standalone bit-array packages are also available for JSON and protobuf. They serialize bits; a complete Bloom filter needs its hash and sizing metadata too.
Know what your answer means.
What is M5.BloomFilter?
M5.BloomFilter is a source-available C# Bloom filter library for .NET 10. It tests whether a key might belong to a set, using a compact bit array instead of storing the original keys.
Can a Bloom filter return a wrong answer?
A positive result means possibly present and can be a false positive. A negative result means definitely absent when the same filter, hash function, and key encoding are used correctly and the bits are intact. Confirm positive matches against your source of truth.
Does 0.01 guarantee a 1% false-positive rate?
No. The requested error rate is a sizing target based on the expected number of elements and the usual hash-distribution assumptions. Inserting more items than planned increases the false-positive probability.
Which hash algorithm does the C# library use by default?
FilterBuilder.Build uses Murmur3KirschMitzenmacher by default. The browser illustration uses a separate simple teaching hash and does not run the C# library.
Can I read older serialized filters?
Version 2 payloads identify the current hash and encoding contract. Older or unversioned payloads must be rebuilt from original keys; changing a version number cannot migrate existing bits.
Who maintains M5.BloomFilter?
M5.BloomFilter is a MILL5 source-available library, built by Richard Crane, Microsoft MVP and founder of MILL5. The source code and issue tracker are on GitHub.
Built by Richard Crane. Microsoft MVP. Founder of MILL5.
From the MILL5 developer tools family: practical building blocks for developers, alongside FastSearch, Pineapple and yocto.