From e30f69bca3deae5d3b01ebdb0795533bcab91d6a Mon Sep 17 00:00:00 2001 From: Brandon Forehand Date: Tue, 16 May 2017 10:21:56 -0700 Subject: [PATCH] Add support for intersection to BloomFilter. --- .../stream/membership/BloomFilter.java | 21 ++++++++ .../stream/membership/BloomFilterTest.java | 48 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/src/main/java/com/clearspring/analytics/stream/membership/BloomFilter.java b/src/main/java/com/clearspring/analytics/stream/membership/BloomFilter.java index a374b088a..a0175f4a9 100644 --- a/src/main/java/com/clearspring/analytics/stream/membership/BloomFilter.java +++ b/src/main/java/com/clearspring/analytics/stream/membership/BloomFilter.java @@ -144,6 +144,27 @@ public Filter merge(Filter... filters) { return merged; } + public void intersectInPlace(BloomFilter other) { + if (this.getHashCount() != other.getHashCount()) { + throw new IllegalArgumentException("Cannot intersect filters of different sizes"); + } + this.filter().and(other.filter()); + } + + public BloomFilter intersect(BloomFilter... filters) { + BloomFilter intersected = new BloomFilter(this.getHashCount(), (BitSet) this.filter().clone()); + + if (filters == null) { + return intersected; + } + + for (BloomFilter filter : filters) { + intersected.intersectInPlace(filter); + } + + return intersected; + } + /** * @return a BloomFilter that always returns a positive match, for testing */ diff --git a/src/test/java/com/clearspring/analytics/stream/membership/BloomFilterTest.java b/src/test/java/com/clearspring/analytics/stream/membership/BloomFilterTest.java index 178d53c6a..828b13593 100644 --- a/src/test/java/com/clearspring/analytics/stream/membership/BloomFilterTest.java +++ b/src/test/java/com/clearspring/analytics/stream/membership/BloomFilterTest.java @@ -84,6 +84,54 @@ public void testMergeException() { BloomFilter mergeBf = (BloomFilter) bf3.merge(bf); } + @Test + public void testIntersectInPlace() { + BloomFilter bf3 = new BloomFilter(ELEMENTS, spec.bucketsPerElement); + for (int i = 0; i < 20; ++i) { + bf.add(Integer.toString(i)); + } + for (int i = 10; i < 30; ++i) { + bf2.add(Integer.toString(i)); + } + for (int i = 10; i < 20; ++i) { + bf3.add(Integer.toString(i)); + } + bf.intersectInPlace(bf2); + for (int i = 0; i < 30; ++i) { + String iString = Integer.toString(i); + if (bf3.isPresent(iString)) { + assertTrue(bf.isPresent(iString)); + } else { + assertFalse(bf.isPresent(iString)); + } + } + } + + @Test(expected=IllegalArgumentException.class) + public void testIntersectInPlaceException() { + BloomFilter bf3 = new BloomFilter(ELEMENTS*10, 1); + bf3.intersectInPlace(bf); + } + + @Test + public void testIntersect() { + for (int i = 0; i < 20; ++i) { + bf.add(Integer.toString(i)); + } + for (int i = 10; i < 30; ++i) { + bf2.add(Integer.toString(i)); + } + BloomFilter bf3 = bf.intersect(bf2); + for (int i = 0; i < 30; ++i) { + String iString = Integer.toString(i); + if (bf.isPresent(iString) && bf2.isPresent(iString)) { + assertTrue(bf3.isPresent(iString)); + } else { + assertFalse(bf3.isPresent(iString)); + } + } + } + @Test public void testFalsePositivesInt() { FilterTest.testFalsePositives(bf, FilterTest.intKeys(), FilterTest.randomKeys2());