diff --git a/src/main/java/org/apache/commons/collections4/bloomfilter/ArrayCountingBloomFilter.java b/src/main/java/org/apache/commons/collections4/bloomfilter/ArrayCountingBloomFilter.java index 128858c037..f5fc946a50 100644 --- a/src/main/java/org/apache/commons/collections4/bloomfilter/ArrayCountingBloomFilter.java +++ b/src/main/java/org/apache/commons/collections4/bloomfilter/ArrayCountingBloomFilter.java @@ -165,6 +165,48 @@ public ArrayCountingBloomFilter copy() { return new ArrayCountingBloomFilter(this); } + @Override + public int getMaxCell() { + return Integer.MAX_VALUE; + } + + @Override + public int getMaxInsert(final CellExtractor cellExtractor) { + final int[] max = {Integer.MAX_VALUE}; + cellExtractor.processCells( (x, y) -> { + final int count = cells[x] / y; + if (count < max[0]) { + max[0] = count; + } + return max[0] > 0; + }); + return max[0]; + } + + @Override + public Shape getShape() { + return shape; + } + + /** + * {@inheritDoc} + * + *

Implementation note + * + *

The state transition to invalid is permanent.

+ * + *

This implementation does not correct negative cells to zero or integer + * overflow cells to {@link Integer#MAX_VALUE}. Thus the operation that + * generated invalid cells can be reversed by using the complement of the + * original operation with the same Bloom filter. This will restore the cells + * to the state prior to the invalid operation. Cells can then be extracted + * using {@link #processCells(CellPredicate)}.

+ */ + @Override + public boolean isValid() { + return state >= 0; + } + @Override public boolean processBitMaps(final LongPredicate consumer) { Objects.requireNonNull(consumer, "consumer"); @@ -215,48 +257,6 @@ public boolean processIndices(final IntPredicate consumer) { return true; } - @Override - public int getMaxCell() { - return Integer.MAX_VALUE; - } - - @Override - public int getMaxInsert(final CellExtractor cellExtractor) { - final int[] max = {Integer.MAX_VALUE}; - cellExtractor.processCells( (x, y) -> { - final int count = cells[x] / y; - if (count < max[0]) { - max[0] = count; - } - return max[0] > 0; - }); - return max[0]; - } - - @Override - public Shape getShape() { - return shape; - } - - /** - * {@inheritDoc} - * - *

Implementation note - * - *

The state transition to invalid is permanent.

- * - *

This implementation does not correct negative cells to zero or integer - * overflow cells to {@link Integer#MAX_VALUE}. Thus the operation that - * generated invalid cells can be reversed by using the complement of the - * original operation with the same Bloom filter. This will restore the cells - * to the state prior to the invalid operation. Cells can then be extracted - * using {@link #processCells(CellPredicate)}.

- */ - @Override - public boolean isValid() { - return state >= 0; - } - @Override public boolean subtract(final CellExtractor other) { Objects.requireNonNull(other, "other"); diff --git a/src/main/java/org/apache/commons/collections4/bloomfilter/BitMapExtractor.java b/src/main/java/org/apache/commons/collections4/bloomfilter/BitMapExtractor.java index 76cbc73958..29aeb80c6a 100644 --- a/src/main/java/org/apache/commons/collections4/bloomfilter/BitMapExtractor.java +++ b/src/main/java/org/apache/commons/collections4/bloomfilter/BitMapExtractor.java @@ -50,6 +50,12 @@ public long[] asBitMapArray() { return Arrays.copyOf(bitMaps, bitMaps.length); } + @Override + public boolean processBitMapPairs(final BitMapExtractor other, final LongBiPredicate func) { + final CountingLongPredicate p = new CountingLongPredicate(bitMaps, func); + return other.processBitMaps(p) && p.processRemaining(); + } + @Override public boolean processBitMaps(final LongPredicate predicate) { for (final long word : bitMaps) { @@ -59,12 +65,6 @@ public boolean processBitMaps(final LongPredicate predicate) { } return true; } - - @Override - public boolean processBitMapPairs(final BitMapExtractor other, final LongBiPredicate func) { - final CountingLongPredicate p = new CountingLongPredicate(bitMaps, func); - return other.processBitMaps(p) && p.processRemaining(); - } }; } @@ -120,21 +120,6 @@ long[] toArray() { return bits.toArray(); } - /** - * Each bit map is passed to the predicate in order. The predicate is applied to each - * bit map value, if the predicate returns {@code false} the execution is stopped, {@code false} - * is returned, and no further bit maps are processed. - * - *

If the extractor is empty this method will return true.

- * - *

Any exceptions thrown by the action are relayed to the caller.

- * - * @param predicate the function to execute - * @return {@code true} if all bit maps returned {@code true}, {@code false} otherwise. - * @throws NullPointerException if the specified consumer is null - */ - boolean processBitMaps(LongPredicate predicate); - /** * Applies the {@code func} to each bit map pair in order. Will apply all of the bit maps from the other * BitMapExtractor to this extractor. If this extractor does not have as many bit maps it will provide 0 (zero) @@ -151,4 +136,19 @@ default boolean processBitMapPairs(final BitMapExtractor other, final LongBiPred final CountingLongPredicate p = new CountingLongPredicate(asBitMapArray(), func); return other.processBitMaps(p) && p.processRemaining(); } + + /** + * Each bit map is passed to the predicate in order. The predicate is applied to each + * bit map value, if the predicate returns {@code false} the execution is stopped, {@code false} + * is returned, and no further bit maps are processed. + * + *

If the extractor is empty this method will return true.

+ * + *

Any exceptions thrown by the action are relayed to the caller.

+ * + * @param predicate the function to execute + * @return {@code true} if all bit maps returned {@code true}, {@code false} otherwise. + * @throws NullPointerException if the specified consumer is null + */ + boolean processBitMaps(LongPredicate predicate); } diff --git a/src/main/java/org/apache/commons/collections4/bloomfilter/BloomFilterExtractor.java b/src/main/java/org/apache/commons/collections4/bloomfilter/BloomFilterExtractor.java index 18e1fa1d28..95dbc6800e 100644 --- a/src/main/java/org/apache/commons/collections4/bloomfilter/BloomFilterExtractor.java +++ b/src/main/java/org/apache/commons/collections4/bloomfilter/BloomFilterExtractor.java @@ -56,16 +56,6 @@ public BloomFilter[] asBloomFilterArray() { return filters.clone(); } - @Override - public boolean processBloomFilters(final Predicate predicate) { - for (final BloomFilter filter : filters) { - if (!predicate.test(filter)) { - return false; - } - } - return true; - } - /** * This implementation uses references to the original filters. Any modifications to the * filters are reflected in the originals. @@ -76,6 +66,16 @@ public boolean processBloomFilterPair(final BloomFilterExtractor other, final CountingPredicate p = new CountingPredicate<>(filters, func); return other.processBloomFilters(p) && p.processRemaining(); } + + @Override + public boolean processBloomFilters(final Predicate predicate) { + for (final BloomFilter filter : filters) { + if (!predicate.test(filter)) { + return false; + } + } + return true; + } }; } @@ -110,16 +110,6 @@ default BloomFilter flatten() { return bf[0]; } - /** - * Executes a Bloom filter Predicate on each Bloom filter in the collection. The - * ordering of the Bloom filters is not specified by this interface. - * - * @param bloomFilterPredicate the predicate to evaluate each Bloom filter with. - * @return {@code false} when the first filter fails the predicate test. Returns - * {@code true} if all filters pass the test. - */ - boolean processBloomFilters(Predicate bloomFilterPredicate); - /** * Applies the {@code func} to each Bloom filter pair in order. Will apply all * of the Bloom filters from the other BloomFilterExtractor to this extractor. If @@ -141,4 +131,14 @@ default boolean processBloomFilterPair(final BloomFilterExtractor other, final CountingPredicate p = new CountingPredicate<>(asBloomFilterArray(), func); return other.processBloomFilters(p) && p.processRemaining(); } + + /** + * Executes a Bloom filter Predicate on each Bloom filter in the collection. The + * ordering of the Bloom filters is not specified by this interface. + * + * @param bloomFilterPredicate the predicate to evaluate each Bloom filter with. + * @return {@code false} when the first filter fails the predicate test. Returns + * {@code true} if all filters pass the test. + */ + boolean processBloomFilters(Predicate bloomFilterPredicate); } diff --git a/src/main/java/org/apache/commons/collections4/bloomfilter/CellExtractor.java b/src/main/java/org/apache/commons/collections4/bloomfilter/CellExtractor.java index 64a8b6631c..89f3944d08 100644 --- a/src/main/java/org/apache/commons/collections4/bloomfilter/CellExtractor.java +++ b/src/main/java/org/apache/commons/collections4/bloomfilter/CellExtractor.java @@ -111,17 +111,6 @@ public int[] asIndexArray() { return counterCells.keySet().stream().mapToInt(c -> c.idx).toArray(); } - @Override - public boolean processCells(final CellPredicate consumer) { - populate(); - for (final CounterCell cell : counterCells.values()) { - if (!consumer.test(cell.idx, cell.count)) { - return false; - } - } - return true; - } - private void populate() { if (counterCells.isEmpty()) { indexExtractor.processIndices(idx -> { @@ -136,6 +125,17 @@ private void populate() { }); } } + + @Override + public boolean processCells(final CellPredicate consumer) { + populate(); + for (final CounterCell cell : counterCells.values()) { + if (!consumer.test(cell.idx, cell.count)) { + return false; + } + } + return true; + } }; } diff --git a/src/main/java/org/apache/commons/collections4/bloomfilter/LayerManager.java b/src/main/java/org/apache/commons/collections4/bloomfilter/LayerManager.java index 2491e08b10..b1300136f6 100644 --- a/src/main/java/org/apache/commons/collections4/bloomfilter/LayerManager.java +++ b/src/main/java/org/apache/commons/collections4/bloomfilter/LayerManager.java @@ -364,24 +364,6 @@ public final T first() { return filters.getFirst(); } - /** - * Executes a Bloom filter Predicate on each Bloom filter in the manager in - * depth order. Oldest filter first. - * - * @param bloomFilterPredicate the predicate to evaluate each Bloom filter with. - * @return {@code false} when the a filter fails the predicate test. Returns - * {@code true} if all filters pass the test. - */ - @Override - public boolean processBloomFilters(final Predicate bloomFilterPredicate) { - for (final BloomFilter bf : filters) { - if (!bloomFilterPredicate.test(bf)) { - return false; - } - } - return true; - } - /** * Gets the Bloom filter at the specified depth. The filter at depth 0 is the * oldest filter. @@ -446,4 +428,22 @@ void next() { this.filterCleanup.accept(filters); addFilter(); } + + /** + * Executes a Bloom filter Predicate on each Bloom filter in the manager in + * depth order. Oldest filter first. + * + * @param bloomFilterPredicate the predicate to evaluate each Bloom filter with. + * @return {@code false} when the a filter fails the predicate test. Returns + * {@code true} if all filters pass the test. + */ + @Override + public boolean processBloomFilters(final Predicate bloomFilterPredicate) { + for (final BloomFilter bf : filters) { + if (!bloomFilterPredicate.test(bf)) { + return false; + } + } + return true; + } } diff --git a/src/main/java/org/apache/commons/collections4/bloomfilter/LayeredBloomFilter.java b/src/main/java/org/apache/commons/collections4/bloomfilter/LayeredBloomFilter.java index ecb18ec3ab..9f5e3e4a43 100644 --- a/src/main/java/org/apache/commons/collections4/bloomfilter/LayeredBloomFilter.java +++ b/src/main/java/org/apache/commons/collections4/bloomfilter/LayeredBloomFilter.java @@ -295,30 +295,6 @@ public BloomFilter flatten() { return bf; } - @Override - public boolean processBitMaps(final LongPredicate predicate) { - return flatten().processBitMaps(predicate); - } - - /** - * Processes the Bloom filters in depth order with the most recent filters - * first. Each filter is passed to the predicate in turn. The function exits on - * the first {@code false} returned by the predicate. - * - * @param bloomFilterPredicate the predicate to execute. - * @return {@code true} if all filters passed the predicate, {@code false} - * otherwise. - */ - @Override - public final boolean processBloomFilters(final Predicate bloomFilterPredicate) { - return layerManager.processBloomFilters(bloomFilterPredicate); - } - - @Override - public boolean processIndices(final IntPredicate predicate) { - return processBloomFilters(bf -> bf.processIndices(predicate)); - } - /** * Gets the Bloom filter at the specified depth * @@ -377,4 +353,28 @@ public void next() { layerManager.next(); } + @Override + public boolean processBitMaps(final LongPredicate predicate) { + return flatten().processBitMaps(predicate); + } + + /** + * Processes the Bloom filters in depth order with the most recent filters + * first. Each filter is passed to the predicate in turn. The function exits on + * the first {@code false} returned by the predicate. + * + * @param bloomFilterPredicate the predicate to execute. + * @return {@code true} if all filters passed the predicate, {@code false} + * otherwise. + */ + @Override + public final boolean processBloomFilters(final Predicate bloomFilterPredicate) { + return layerManager.processBloomFilters(bloomFilterPredicate); + } + + @Override + public boolean processIndices(final IntPredicate predicate) { + return processBloomFilters(bf -> bf.processIndices(predicate)); + } + } diff --git a/src/main/java/org/apache/commons/collections4/bloomfilter/SimpleBloomFilter.java b/src/main/java/org/apache/commons/collections4/bloomfilter/SimpleBloomFilter.java index 0668ede93f..2a0b1527f3 100644 --- a/src/main/java/org/apache/commons/collections4/bloomfilter/SimpleBloomFilter.java +++ b/src/main/java/org/apache/commons/collections4/bloomfilter/SimpleBloomFilter.java @@ -101,29 +101,6 @@ public SimpleBloomFilter copy() { return new SimpleBloomFilter(this); } - @Override - public boolean processBitMaps(final LongPredicate consumer) { - Objects.requireNonNull(consumer, "consumer"); - for (final long l : bitMap) { - if (!consumer.test(l)) { - return false; - } - } - return true; - } - - @Override - public boolean processBitMapPairs(final BitMapExtractor other, final LongBiPredicate func) { - final CountingLongPredicate p = new CountingLongPredicate(bitMap, func); - return other.processBitMaps(p) && p.processRemaining(); - } - - @Override - public boolean processIndices(final IntPredicate consumer) { - Objects.requireNonNull(consumer, "consumer"); - return IndexExtractor.fromBitMapExtractor(this).processIndices(consumer); - } - @Override public Shape getShape() { return shape; @@ -193,4 +170,27 @@ public boolean merge(final IndexExtractor indexExtractor) { cardinality = -1; return true; } + + @Override + public boolean processBitMapPairs(final BitMapExtractor other, final LongBiPredicate func) { + final CountingLongPredicate p = new CountingLongPredicate(bitMap, func); + return other.processBitMaps(p) && p.processRemaining(); + } + + @Override + public boolean processBitMaps(final LongPredicate consumer) { + Objects.requireNonNull(consumer, "consumer"); + for (final long l : bitMap) { + if (!consumer.test(l)) { + return false; + } + } + return true; + } + + @Override + public boolean processIndices(final IntPredicate consumer) { + Objects.requireNonNull(consumer, "consumer"); + return IndexExtractor.fromBitMapExtractor(this).processIndices(consumer); + } } diff --git a/src/main/java/org/apache/commons/collections4/bloomfilter/SparseBloomFilter.java b/src/main/java/org/apache/commons/collections4/bloomfilter/SparseBloomFilter.java index 0df154e701..78b415fa89 100644 --- a/src/main/java/org/apache/commons/collections4/bloomfilter/SparseBloomFilter.java +++ b/src/main/java/org/apache/commons/collections4/bloomfilter/SparseBloomFilter.java @@ -103,6 +103,54 @@ public SparseBloomFilter copy() { return new SparseBloomFilter(this); } + @Override + public Shape getShape() { + return shape; + } + + @Override + public boolean isEmpty() { + return indices.isEmpty(); + } + + @Override + public boolean merge(final BitMapExtractor bitMapExtractor) { + Objects.requireNonNull(bitMapExtractor, "bitMapExtractor"); + return this.merge(IndexExtractor.fromBitMapExtractor(bitMapExtractor)); + } + + @Override + public boolean merge(final BloomFilter other) { + Objects.requireNonNull(other, "other"); + final IndexExtractor indexExtractor = (other.characteristics() & SPARSE) != 0 ? (IndexExtractor) other : IndexExtractor.fromBitMapExtractor(other); + merge(indexExtractor); + return true; + } + + @Override + public boolean merge(final Hasher hasher) { + Objects.requireNonNull(hasher, "hasher"); + merge(hasher.indices(shape)); + return true; + } + + @Override + public boolean merge(final IndexExtractor indexExtractor) { + Objects.requireNonNull(indexExtractor, "indexExtractor"); + indexExtractor.processIndices(this::add); + if (!this.indices.isEmpty()) { + if (this.indices.last() >= shape.getNumberOfBits()) { + throw new IllegalArgumentException(String.format("Value in list %s is greater than maximum value (%s)", + this.indices.last(), shape.getNumberOfBits() - 1)); + } + if (this.indices.first() < 0) { + throw new IllegalArgumentException( + String.format("Value in list %s is less than 0", this.indices.first())); + } + } + return true; + } + @Override public boolean processBitMaps(final LongPredicate consumer) { Objects.requireNonNull(consumer, "consumer"); @@ -151,52 +199,4 @@ public boolean processIndices(final IntPredicate consumer) { } return true; } - - @Override - public Shape getShape() { - return shape; - } - - @Override - public boolean isEmpty() { - return indices.isEmpty(); - } - - @Override - public boolean merge(final BitMapExtractor bitMapExtractor) { - Objects.requireNonNull(bitMapExtractor, "bitMapExtractor"); - return this.merge(IndexExtractor.fromBitMapExtractor(bitMapExtractor)); - } - - @Override - public boolean merge(final BloomFilter other) { - Objects.requireNonNull(other, "other"); - final IndexExtractor indexExtractor = (other.characteristics() & SPARSE) != 0 ? (IndexExtractor) other : IndexExtractor.fromBitMapExtractor(other); - merge(indexExtractor); - return true; - } - - @Override - public boolean merge(final Hasher hasher) { - Objects.requireNonNull(hasher, "hasher"); - merge(hasher.indices(shape)); - return true; - } - - @Override - public boolean merge(final IndexExtractor indexExtractor) { - Objects.requireNonNull(indexExtractor, "indexExtractor"); - indexExtractor.processIndices(this::add); - if (!this.indices.isEmpty()) { - if (this.indices.last() >= shape.getNumberOfBits()) { - throw new IllegalArgumentException(String.format("Value in list %s is greater than maximum value (%s)", - this.indices.last(), shape.getNumberOfBits() - 1)); - } - if (this.indices.first() < 0) { - throw new IllegalArgumentException( - String.format("Value in list %s is less than 0", this.indices.first())); - } - } - return true; - } } diff --git a/src/main/java/org/apache/commons/collections4/bloomfilter/WrappedBloomFilter.java b/src/main/java/org/apache/commons/collections4/bloomfilter/WrappedBloomFilter.java index bd4435f229..23afeee3a8 100644 --- a/src/main/java/org/apache/commons/collections4/bloomfilter/WrappedBloomFilter.java +++ b/src/main/java/org/apache/commons/collections4/bloomfilter/WrappedBloomFilter.java @@ -96,21 +96,6 @@ public int estimateUnion(final BloomFilter other) { return wrapped.estimateUnion(other); } - @Override - public boolean processBitMaps(final LongPredicate predicate) { - return wrapped.processBitMaps(predicate); - } - - @Override - public boolean processBitMapPairs(final BitMapExtractor other, final LongBiPredicate func) { - return wrapped.processBitMapPairs(other, func); - } - - @Override - public boolean processIndices(final IntPredicate predicate) { - return wrapped.processIndices(predicate); - } - @Override public Shape getShape() { return wrapped.getShape(); @@ -149,4 +134,19 @@ public boolean merge(final Hasher hasher) { public boolean merge(final IndexExtractor indexExtractor) { return wrapped.merge(indexExtractor); } + + @Override + public boolean processBitMapPairs(final BitMapExtractor other, final LongBiPredicate func) { + return wrapped.processBitMapPairs(other, func); + } + + @Override + public boolean processBitMaps(final LongPredicate predicate) { + return wrapped.processBitMaps(predicate); + } + + @Override + public boolean processIndices(final IntPredicate predicate) { + return wrapped.processIndices(predicate); + } } diff --git a/src/test/java/org/apache/commons/collections4/bloomfilter/AbstractHasherTest.java b/src/test/java/org/apache/commons/collections4/bloomfilter/AbstractHasherTest.java index 98452c50a3..04c04f3c2a 100644 --- a/src/test/java/org/apache/commons/collections4/bloomfilter/AbstractHasherTest.java +++ b/src/test/java/org/apache/commons/collections4/bloomfilter/AbstractHasherTest.java @@ -24,20 +24,20 @@ public abstract class AbstractHasherTest extends AbstractIndexExtractorTest { - protected abstract Hasher createEmptyHasher(); - @Override protected IndexExtractor createEmptyExtractor() { return createEmptyHasher().indices(getTestShape()); } - protected abstract Hasher createHasher(); + protected abstract Hasher createEmptyHasher(); @Override protected IndexExtractor createExtractor() { return createHasher().indices(getTestShape()); } + protected abstract Hasher createHasher(); + /** * A method to get the number of items in a hasher. Mostly applies to * Collections of hashers. diff --git a/src/test/java/org/apache/commons/collections4/bloomfilter/DefaultBloomFilterTest.java b/src/test/java/org/apache/commons/collections4/bloomfilter/DefaultBloomFilterTest.java index e67201043d..081bc0fbca 100644 --- a/src/test/java/org/apache/commons/collections4/bloomfilter/DefaultBloomFilterTest.java +++ b/src/test/java/org/apache/commons/collections4/bloomfilter/DefaultBloomFilterTest.java @@ -73,21 +73,6 @@ public boolean contains(final IndexExtractor indexExtractor) { return indexExtractor.processIndices(indices::contains); } - @Override - public boolean processBitMaps(final LongPredicate consumer) { - return BitMapExtractor.fromIndexExtractor(this, shape.getNumberOfBits()).processBitMaps(consumer); - } - - @Override - public boolean processIndices(final IntPredicate consumer) { - for (final Integer i : indices) { - if (!consumer.test(i)) { - return false; - } - } - return true; - } - @Override public Shape getShape() { return shape; @@ -107,6 +92,21 @@ public boolean merge(final IndexExtractor indexExtractor) { checkIndicesRange(); return result; } + + @Override + public boolean processBitMaps(final LongPredicate consumer) { + return BitMapExtractor.fromIndexExtractor(this, shape.getNumberOfBits()).processBitMaps(consumer); + } + + @Override + public boolean processIndices(final IntPredicate consumer) { + for (final Integer i : indices) { + if (!consumer.test(i)) { + return false; + } + } + return true; + } } static class BrokenCardinality extends NonSparseDefaultBloomFilter { diff --git a/src/test/java/org/apache/commons/collections4/bloomfilter/IndexExtractorTest.java b/src/test/java/org/apache/commons/collections4/bloomfilter/IndexExtractorTest.java index 82010ca490..40cab92e30 100644 --- a/src/test/java/org/apache/commons/collections4/bloomfilter/IndexExtractorTest.java +++ b/src/test/java/org/apache/commons/collections4/bloomfilter/IndexExtractorTest.java @@ -47,6 +47,19 @@ public boolean processBitMaps(final LongPredicate consumer) { } } + @ParameterizedTest + @ValueSource(ints = {32, 33}) + void testAsIndexArray(final int n) { + final IndexExtractor ip = i -> { + for (int j = 0; j < n; j++) { + // Always test index zero + i.test(0); + } + return true; + }; + Assertions.assertArrayEquals(new int[n], ip.asIndexArray()); + } + @Test public void testFromBitMapExtractor() { TestingBitMapExtractor testingBitMapExtractor = new TestingBitMapExtractor(new long[] {1L, 2L, 3L}); @@ -71,17 +84,4 @@ public void testFromBitMapExtractor() { assertEquals(Integer.valueOf(i), lst.get(i)); } } - - @ParameterizedTest - @ValueSource(ints = {32, 33}) - void testAsIndexArray(final int n) { - final IndexExtractor ip = i -> { - for (int j = 0; j < n; j++) { - // Always test index zero - i.test(0); - } - return true; - }; - Assertions.assertArrayEquals(new int[n], ip.asIndexArray()); - } }