001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.collections4.bloomfilter; 018 019import java.util.Objects; 020 021/** 022 * The interface that describes a Bloom filter. 023 * <p> 024 * <em>See implementation notes for {@link BitMapExtractor} and {@link IndexExtractor}.</em> 025 * </p> 026 * <p> 027 * The {@code merge} operations enable bit indexes one at a time and are not atomic. If an index is out of 028 * range an {@link IllegalArgumentException} is raised, but indexes processed before the bad one may already 029 * have been enabled. By design the filter is not rolled back: such an exception signals misuse with bad 030 * indexes and must not be ignored. A filter that throws during a merge should be considered invalid, since 031 * it may hold bits that do not correspond to any valid hashed value. 032 * </p> 033 * 034 * @param <T> The BloomFilter type. 035 * @see BitMapExtractor 036 * @see IndexExtractor 037 * @since 4.5.0-M1 038 */ 039public interface BloomFilter<T extends BloomFilter<T>> extends IndexExtractor, BitMapExtractor { 040 041 /** 042 * The sparse characteristic used to determine the best method for matching: {@value}. 043 * <p> 044 * For `sparse` implementations the {@code forEachIndex(IntConsumer consumer)} method is more efficient. For non `sparse` implementations the 045 * {@code forEachBitMap(LongConsumer consumer)} is more efficient. Implementers should determine if it is easier. 046 * </p> 047 */ 048 int SPARSE = 0x1; 049 050 /** 051 * Gets the cardinality (number of enabled bits) of this Bloom filter. 052 * 053 * <p>This is also known as the Hamming value or Hamming number.</p> 054 * 055 * @return The cardinality of this filter 056 */ 057 int cardinality(); 058 059 // Query Operations 060 061 /** 062 * Gets the characteristics of the filter. 063 * <p> 064 * Characteristics are defined as bits within the characteristics integer. 065 * </p> 066 * 067 * @return The characteristics for this bloom filter. 068 */ 069 int characteristics(); 070 071 /** 072 * Clears the filter to by resetting it to its initial, unpopulated state. 073 */ 074 void clear(); 075 076 /** 077 * Returns {@code true} if this filter contains the bits specified in the bit maps produced by the 078 * bitMapExtractor. 079 * 080 * @param bitMapExtractor The {@code BitMapExtractor} to provide the bit maps. 081 * @return {@code true} if this filter is enabled for all bits specified by the bit maps 082 */ 083 default boolean contains(final BitMapExtractor bitMapExtractor) { 084 return processBitMapPairs(bitMapExtractor, (x, y) -> (x & y) == y); 085 } 086 087 /** 088 * Returns {@code true} if this filter contains the specified filter. 089 * 090 * <p>Specifically this 091 * returns {@code true} if this filter is enabled for all bits that are enabled in the 092 * {@code other} filter. Using the bit representations this is 093 * effectively {@code (this AND other) == other}.</p> 094 * 095 * @param other The other Bloom filter 096 * @return true if all enabled bits in the other filter are enabled in this filter. 097 */ 098 default boolean contains(final BloomFilter<?> other) { 099 Objects.requireNonNull(other, "other"); 100 return (characteristics() & SPARSE) != 0 ? contains((IndexExtractor) other) : contains((BitMapExtractor) other); 101 } 102 103 /** 104 * Returns {@code true} if this filter contains the bits specified in the hasher. 105 * 106 * <p>Specifically this returns {@code true} if this filter is enabled for all bit indexes 107 * identified by the {@code hasher}. Using the bit map representations this is 108 * effectively {@code (this AND hasher) == hasher}.</p> 109 * 110 * @param hasher The hasher to provide the indexes 111 * @return true if this filter is enabled for all bits specified by the hasher 112 */ 113 default boolean contains(final Hasher hasher) { 114 Objects.requireNonNull(hasher, "Hasher"); 115 final Shape shape = getShape(); 116 return contains(hasher.indices(shape)); 117 } 118 119 /** 120 * Returns {@code true} if this filter contains the indices specified IndexExtractor. 121 * 122 * <p>Specifically this returns {@code true} if this filter is enabled for all bit indexes 123 * identified by the {@code IndexExtractor}.</p> 124 * 125 * @param indexExtractor The IndexExtractor to provide the indexes 126 * @return {@code true} if this filter is enabled for all bits specified by the IndexExtractor 127 */ 128 boolean contains(IndexExtractor indexExtractor); 129 130 /** 131 * Creates a new instance of this {@link BloomFilter} with the same properties as the current one. 132 * 133 * @return A copy of this {@link BloomFilter}. 134 */ 135 T copy(); 136 137 // update operations 138 139 /** 140 * Estimates the number of items in the intersection of this Bloom filter with the other bloom filter. 141 * 142 * <p>This method produces estimate is roughly equivalent to the number of unique Hashers that have been merged into both 143 * of the filters by rounding the value from the calculation described in the {@link Shape} class Javadoc.</p> 144 * 145 * <p><em>{@code estimateIntersection} should only be called with Bloom filters of the same Shape. If called on Bloom 146 * filters of differing shape this method is not symmetric. If {@code other} has more bits an {@code IllegalArgumentException} 147 * may be thrown.</em></p> 148 * 149 * @param other The other Bloom filter 150 * @return An estimate of the number of items in the intersection. If the calculated estimate is larger than Integer.MAX_VALUE then MAX_VALUE is returned. 151 * @throws IllegalArgumentException if the estimated N for the union of the filters is infinite. 152 * @see #estimateN() 153 * @see Shape 154 */ 155 default int estimateIntersection(final BloomFilter<?> other) { 156 Objects.requireNonNull(other, "other"); 157 final double eThis = getShape().estimateN(cardinality()); 158 final double eOther = getShape().estimateN(other.cardinality()); 159 if (Double.isInfinite(eThis) && Double.isInfinite(eOther)) { 160 // if both are infinite the union is infinite and we return Integer.MAX_VALUE 161 return Integer.MAX_VALUE; 162 } 163 long estimate; 164 // if one is infinite the intersection is the other. 165 if (Double.isInfinite(eThis)) { 166 estimate = Math.round(eOther); 167 } else if (Double.isInfinite(eOther)) { 168 estimate = Math.round(eThis); 169 } else { 170 final T union = this.copy(); 171 union.merge(other); 172 final double eUnion = getShape().estimateN(union.cardinality()); 173 if (Double.isInfinite(eUnion)) { 174 throw new IllegalArgumentException("The estimated N for the union of the filters is infinite"); 175 } 176 // maximum estimate value using integer values is: 46144189292 thus 177 // eThis + eOther cannot overflow the long value. 178 estimate = Math.round(eThis + eOther - eUnion); 179 estimate = estimate < 0 ? 0 : estimate; 180 } 181 return estimate > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) estimate; 182 } 183 184 /** 185 * Estimates the number of items in the Bloom filter. 186 * 187 * <p>By default this is the rounding of the {@code Shape.estimateN(cardinality)} calculation for the 188 * shape and cardinality of this filter.</p> 189 * 190 * <p>This produces an estimate roughly equivalent to the number of Hashers that have been merged into the filter 191 * by rounding the value from the calculation described in the {@link Shape} class Javadoc.</p> 192 * 193 * <p><em>Note:</em></p> 194 * <ul> 195 * <li>if cardinality == numberOfBits, then result is Integer.MAX_VALUE.</li> 196 * <li>if cardinality > numberOfBits, then an IllegalArgumentException is thrown.</li> 197 * </ul> 198 * 199 * @return An estimate of the number of items in the bloom filter. Will return Integer.MAX_VALUE if the 200 * estimate is larger than Integer.MAX_VALUE. 201 * @throws IllegalArgumentException if the cardinality is > numberOfBits as defined in Shape. 202 * @see Shape#estimateN(int) 203 * @see Shape 204 */ 205 default int estimateN() { 206 final double d = getShape().estimateN(cardinality()); 207 if (Double.isInfinite(d)) { 208 return Integer.MAX_VALUE; 209 } 210 if (Double.isNaN(d)) { 211 throw new IllegalArgumentException("Cardinality too large: " + cardinality()); 212 } 213 final long l = Math.round(d); 214 return l > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) l; 215 } 216 217 /** 218 * Estimates the number of items in the union of this Bloom filter with the other bloom filter. 219 * 220 * <p>This produces an estimate roughly equivalent to the number of unique Hashers that have been merged into either 221 * of the filters by rounding the value from the calculation described in the {@link Shape} class Javadoc.</p> 222 * 223 * <p><em>{@code estimateUnion} should only be called with Bloom filters of the same Shape. If called on Bloom 224 * filters of differing shape this method is not symmetric. If {@code other} has more bits an {@code IllegalArgumentException} 225 * may be thrown.</em></p> 226 * 227 * @param other The other Bloom filter 228 * @return An estimate of the number of items in the union. Will return Integer.MAX_VALUE if the 229 * estimate is larger than Integer.MAX_VALUE. 230 * @see #estimateN() 231 * @see Shape 232 */ 233 default int estimateUnion(final BloomFilter<?> other) { 234 Objects.requireNonNull(other, "other"); 235 final T copy = this.copy(); 236 copy.merge(other); 237 return copy.estimateN(); 238 } 239 240 /** 241 * Gets the shape that was used when the filter was built. 242 * 243 * @return The shape the filter was built with. 244 */ 245 Shape getShape(); 246 247 // Counting Operations 248 249 /** 250 * Determines if all the bits are off. This is equivalent to 251 * {@code cardinality() == 0}. 252 * 253 * <p> 254 * <em>Note: This method is optimized for non-sparse filters.</em> Implementers 255 * are encouraged to implement faster checks if possible. 256 * </p> 257 * 258 * @return {@code true} if no bits are enabled, {@code false} otherwise. 259 */ 260 default boolean isEmpty() { 261 return processBitMaps(y -> y == 0); 262 } 263 264 /** 265 * Determines if the bloom filter is "full". 266 * 267 * <p>Full is defined as having no unset bits.</p> 268 * 269 * @return {@code true} if the filter is full, {@code false} otherwise. 270 */ 271 default boolean isFull() { 272 return cardinality() == getShape().getNumberOfBits(); 273 } 274 275 /** 276 * Merges the specified hasher into this Bloom filter. Specifically all 277 * bit indexes that are identified by the {@code bitMapExtractor} will be enabled in this filter. 278 * 279 * <p><em>Note: This method should return {@code true} even if no additional bit indexes were 280 * enabled. A {@code false} result indicates that this filter may or may not contain all the indexes 281 * enabled in the {@code bitMapExtractor}.</em> This state may occur in complex Bloom filter implementations like 282 * counting Bloom filters.</p> 283 * 284 * @param bitMapExtractor The BitMapExtractor to merge. 285 * @return true if the merge was successful 286 * @throws IllegalArgumentException if bitMapExtractor sends illegal value. 287 */ 288 boolean merge(BitMapExtractor bitMapExtractor); 289 290 /** 291 * Merges the specified Bloom filter into this Bloom filter. 292 * 293 * <p>Specifically all 294 * bit indexes that are identified by the {@code other} will be enabled in this filter.</p> 295 * 296 * <p><em>Note: This method should return {@code true} even if no additional bit indexes were 297 * enabled. A {@code false} result indicates that this filter may or may not contain 298 * the {@code other} Bloom filter.</em> This state may occur in complex Bloom filter implementations like 299 * counting Bloom filters.</p> 300 * 301 * @param other The bloom filter to merge into this one. 302 * @return true if the merge was successful 303 */ 304 default boolean merge(final BloomFilter<?> other) { 305 return (characteristics() & SPARSE) != 0 ? merge((IndexExtractor) other) : merge((BitMapExtractor) other); 306 } 307 308 /** 309 * Merges the specified hasher into this Bloom filter. Specifically all 310 * bit indexes that are identified by the {@code hasher} will be enabled in this filter. 311 * 312 * <p><em>Note: This method should return {@code true} even if no additional bit indexes were 313 * enabled. A {@code false} result indicates that this filter may or may not contain 314 * the {@code hasher} values.</em> This state may occur in complex Bloom filter implementations like 315 * counting Bloom filters.</p> 316 * 317 * @param hasher The hasher to merge. 318 * @return true if the merge was successful 319 * @throws IllegalArgumentException if hasher produces an illegal value. 320 */ 321 default boolean merge(final Hasher hasher) { 322 Objects.requireNonNull(hasher, "hasher"); 323 return merge(hasher.indices(getShape())); 324 } 325 326 /** 327 * Merges the specified IndexExtractor into this Bloom filter. Specifically all 328 * bit indexes that are identified by the {@code indexExtractor} will be enabled in this filter. 329 * 330 * <p><em>Note: This method should return {@code true} even if no additional bit indexes were 331 * enabled. A {@code false} result indicates that this filter may or may not contain all the indexes of 332 * the {@code indexExtractor}.</em> This state may occur in complex Bloom filter implementations like 333 * counting Bloom filters.</p> 334 * 335 * @param indexExtractor The IndexExtractor to merge. 336 * @return true if the merge was successful 337 * @throws IllegalArgumentException if indexExtractor sends illegal value. 338 */ 339 boolean merge(IndexExtractor indexExtractor); 340 341 /** 342 * Most Bloom filters create unique IndexExtractors. 343 */ 344 @Override 345 default IndexExtractor uniqueIndices() { 346 return this; 347 } 348}