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 * http://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.Arrays; 020import java.util.BitSet; 021import java.util.Objects; 022import java.util.function.IntPredicate; 023import java.util.function.LongPredicate; 024 025/** 026 * An object that produces indices of a Bloom filter. 027 * <p><em> 028 * The default implementation of {@code asIndexArray} is slow. Implementers should reimplement the 029 * method where possible.</em></p> 030 * 031 * @since 4.5.0-M2 032 */ 033@FunctionalInterface 034public interface IndexExtractor { 035 036 /** 037 * Creates an IndexExtractor from a {@code BitMapExtractor}. 038 * 039 * @param bitMapExtractor the {@code BitMapExtractor} 040 * @return a new {@code IndexExtractor}. 041 */ 042 static IndexExtractor fromBitMapExtractor(final BitMapExtractor bitMapExtractor) { 043 Objects.requireNonNull(bitMapExtractor, "bitMapExtractor"); 044 return consumer -> { 045 final LongPredicate longPredicate = new LongPredicate() { 046 int wordIdx; 047 048 @Override 049 public boolean test(long word) { 050 int i = wordIdx; 051 while (word != 0) { 052 if ((word & 1) == 1 && !consumer.test(i)) { 053 return false; 054 } 055 word >>>= 1; 056 i++; 057 } 058 wordIdx += 64; 059 return true; 060 } 061 }; 062 return bitMapExtractor.processBitMaps(longPredicate::test); 063 }; 064 } 065 066 /** 067 * Creates an IndexExtractor from an array of integers. 068 * 069 * @param values the index values 070 * @return an IndexExtractor that uses the values. 071 */ 072 static IndexExtractor fromIndexArray(final int... values) { 073 return new IndexExtractor() { 074 075 @Override 076 public int[] asIndexArray() { 077 return values.clone(); 078 } 079 080 @Override 081 public boolean processIndices(final IntPredicate predicate) { 082 for (final int value : values) { 083 if (!predicate.test(value)) { 084 return false; 085 } 086 } 087 return true; 088 } 089 }; 090 } 091 092 /** 093 * Return a copy of the IndexExtractor data as an int array. 094 * 095 * <p>Indices ordering and uniqueness is not guaranteed.</p> 096 * 097 * <p><em> 098 * The default implementation of this method creates an array and populates 099 * it. Implementations that have access to an index array should consider 100 * returning a copy of that array if possible. 101 * </em></p> 102 * 103 * @return An int array of the data. 104 */ 105 default int[] asIndexArray() { 106 class Indices { 107 private int[] data = new int[32]; 108 private int size; 109 110 boolean add(final int index) { 111 data = IndexUtils.ensureCapacityForAdd(data, size); 112 data[size++] = index; 113 return true; 114 } 115 116 int[] toArray() { 117 // Edge case to avoid a large array copy 118 return size == data.length ? data : Arrays.copyOf(data, size); 119 } 120 } 121 final Indices indices = new Indices(); 122 processIndices(indices::add); 123 return indices.toArray(); 124 } 125 126 /** 127 * Each index is passed to the predicate. The predicate is applied to each 128 * index value, if the predicate returns {@code false} the execution is stopped, {@code false} 129 * is returned, and no further indices are processed. 130 * 131 * <p>Any exceptions thrown by the action are relayed to the caller.</p> 132 * 133 * <p>Indices ordering and uniqueness is not guaranteed.</p> 134 * 135 * @param predicate the action to be performed for each non-zero bit index. 136 * @return {@code true} if all indexes return true from consumer, {@code false} otherwise. 137 * @throws NullPointerException if the specified action is null 138 */ 139 boolean processIndices(IntPredicate predicate); 140 141 /** 142 * Creates an IndexExtractor comprising the unique indices for this extractor. 143 * 144 * <p>By default creates a new extractor with some overhead to remove 145 * duplicates. IndexExtractors that return unique indices by default 146 * should override this to return {@code this}.</p> 147 * 148 * <p>The default implementation will filter the indices from this instance 149 * and return them in ascending order.</p> 150 * 151 * @return the IndexExtractor of unique values. 152 * @throws IndexOutOfBoundsException if any index is less than zero. 153 */ 154 default IndexExtractor uniqueIndices() { 155 final BitSet bitSet = new BitSet(); 156 processIndices(i -> { 157 bitSet.set(i); 158 return true; 159 }); 160 161 return new IndexExtractor() { 162 @Override 163 public boolean processIndices(final IntPredicate predicate) { 164 for (int idx = bitSet.nextSetBit(0); idx >= 0; idx = bitSet.nextSetBit(idx + 1)) { 165 if (!predicate.test(idx)) { 166 return false; 167 } 168 } 169 return true; 170 } 171 172 @Override 173 public IndexExtractor uniqueIndices() { 174 return this; 175 } 176 }; 177 } 178}