IndexExtractor.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.collections4.bloomfilter;

  18. import java.util.Arrays;
  19. import java.util.BitSet;
  20. import java.util.Objects;
  21. import java.util.function.IntPredicate;
  22. import java.util.function.LongPredicate;

  23. /**
  24.  * An object that produces indices of a Bloom filter.
  25.  * <p><em>
  26.  * The default implementation of {@code asIndexArray} is slow. Implementers should reimplement the
  27.  * method where possible.</em></p>
  28.  *
  29.  * @since 4.5.0-M2
  30.  */
  31. @FunctionalInterface
  32. public interface IndexExtractor {

  33.     /**
  34.      * Creates an IndexExtractor from a {@code BitMapExtractor}.
  35.      *
  36.      * @param bitMapExtractor the {@code BitMapExtractor}
  37.      * @return a new {@code IndexExtractor}.
  38.      */
  39.     static IndexExtractor fromBitMapExtractor(final BitMapExtractor bitMapExtractor) {
  40.         Objects.requireNonNull(bitMapExtractor, "bitMapExtractor");
  41.         return consumer -> {
  42.             final LongPredicate longPredicate = new LongPredicate() {
  43.                 int wordIdx;

  44.                 @Override
  45.                 public boolean test(long word) {
  46.                     int i = wordIdx;
  47.                     while (word != 0) {
  48.                         if ((word & 1) == 1 && !consumer.test(i)) {
  49.                             return false;
  50.                         }
  51.                         word >>>= 1;
  52.                         i++;
  53.                     }
  54.                     wordIdx += 64;
  55.                     return true;
  56.                 }
  57.             };
  58.             return bitMapExtractor.processBitMaps(longPredicate::test);
  59.         };
  60.     }

  61.     /**
  62.      * Creates an IndexExtractor from an array of integers.
  63.      *
  64.      * @param values the index values
  65.      * @return an IndexExtractor that uses the values.
  66.      */
  67.     static IndexExtractor fromIndexArray(final int... values) {
  68.         return new IndexExtractor() {

  69.             @Override
  70.             public int[] asIndexArray() {
  71.                 return values.clone();
  72.             }

  73.             @Override
  74.             public boolean processIndices(final IntPredicate predicate) {
  75.                 for (final int value : values) {
  76.                     if (!predicate.test(value)) {
  77.                         return false;
  78.                     }
  79.                 }
  80.                 return true;
  81.             }
  82.         };
  83.     }

  84.     /**
  85.      * Return a copy of the IndexExtractor data as an int array.
  86.      *
  87.      * <p>Indices ordering and uniqueness is not guaranteed.</p>
  88.      *
  89.      * <p><em>
  90.      * The default implementation of this method creates an array and populates
  91.      * it.  Implementations that have access to an index array should consider
  92.      * returning a copy of that array if possible.
  93.      * </em></p>
  94.      *
  95.      * @return An int array of the data.
  96.      */
  97.     default int[] asIndexArray() {
  98.         final class Indices {
  99.             private int[] data = new int[32];
  100.             private int size;

  101.             boolean add(final int index) {
  102.                 data = IndexUtils.ensureCapacityForAdd(data, size);
  103.                 data[size++] = index;
  104.                 return true;
  105.             }

  106.             int[] toArray() {
  107.                 // Edge case to avoid a large array copy
  108.                 return size == data.length ? data : Arrays.copyOf(data, size);
  109.             }
  110.         }
  111.         final Indices indices = new Indices();
  112.         processIndices(indices::add);
  113.         return indices.toArray();
  114.     }

  115.     /**
  116.      * Each index is passed to the predicate. The predicate is applied to each
  117.      * index value, if the predicate returns {@code false} the execution is stopped, {@code false}
  118.      * is returned, and no further indices are processed.
  119.      *
  120.      * <p>Any exceptions thrown by the action are relayed to the caller.</p>
  121.      *
  122.      * <p>Indices ordering and uniqueness is not guaranteed.</p>
  123.      *
  124.      * @param predicate the action to be performed for each non-zero bit index.
  125.      * @return {@code true} if all indexes return true from consumer, {@code false} otherwise.
  126.      * @throws NullPointerException if the specified action is null
  127.      */
  128.     boolean processIndices(IntPredicate predicate);

  129.     /**
  130.      * Creates an IndexExtractor comprising the unique indices for this extractor.
  131.      *
  132.      * <p>By default creates a new extractor with some overhead to remove
  133.      * duplicates.  IndexExtractors that return unique indices by default
  134.      * should override this to return {@code this}.</p>
  135.      *
  136.      * <p>The default implementation will filter the indices from this instance
  137.      * and return them in ascending order.</p>
  138.      *
  139.      * @return the IndexExtractor of unique values.
  140.      * @throws IndexOutOfBoundsException if any index is less than zero.
  141.      */
  142.     default IndexExtractor uniqueIndices() {
  143.         final BitSet bitSet = new BitSet();
  144.         processIndices(i -> {
  145.             bitSet.set(i);
  146.             return true;
  147.         });

  148.         return new IndexExtractor() {
  149.             @Override
  150.             public boolean processIndices(final IntPredicate predicate) {
  151.                 for (int idx = bitSet.nextSetBit(0); idx >= 0; idx = bitSet.nextSetBit(idx + 1)) {
  152.                     if (!predicate.test(idx)) {
  153.                         return false;
  154.                     }
  155.                 }
  156.                 return true;
  157.             }

  158.             @Override
  159.             public IndexExtractor uniqueIndices() {
  160.                 return this;
  161.             }
  162.         };
  163.     }
  164. }