1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.collections4.bloomfilter;
18
19 import java.util.Arrays;
20 import java.util.Objects;
21 import java.util.function.IntPredicate;
22 import java.util.function.LongPredicate;
23 import java.util.stream.IntStream;
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public final class ArrayCountingBloomFilter implements CountingBloomFilter {
51
52
53
54
55 private final Shape shape;
56
57
58
59
60 private final int[] cells;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 private int state;
87
88 private ArrayCountingBloomFilter(final ArrayCountingBloomFilter source) {
89 this.shape = source.shape;
90 this.state = source.state;
91 this.cells = source.cells.clone();
92 }
93
94
95
96
97
98
99 public ArrayCountingBloomFilter(final Shape shape) {
100 Objects.requireNonNull(shape, "shape");
101 this.shape = shape;
102 cells = new int[shape.getNumberOfBits()];
103 }
104
105 @Override
106 public boolean add(final CellExtractor other) {
107 Objects.requireNonNull(other, "other");
108 other.processCells(this::add);
109 return isValid();
110 }
111
112
113
114
115
116
117
118
119 private boolean add(final int idx, final int addend) {
120 try {
121 final int updated = cells[idx] + addend;
122 state |= updated;
123 cells[idx] = updated;
124 return true;
125 } catch (final IndexOutOfBoundsException e) {
126 throw new IllegalArgumentException(
127 String.format("Filter only accepts values in the [0,%d) range", getShape().getNumberOfBits()), e);
128 }
129 }
130
131 @Override
132 public int[] asIndexArray() {
133 return IntStream.range(0, cells.length).filter(i -> cells[i] > 0).toArray();
134 }
135
136 @Override
137 public int cardinality() {
138 return (int) IntStream.range(0, cells.length).filter(i -> cells[i] > 0).count();
139 }
140
141 @Override
142 public int characteristics() {
143 return SPARSE;
144 }
145
146 @Override
147 public void clear() {
148 Arrays.fill(cells, 0);
149 }
150
151 @Override
152 public boolean contains(final BitMapExtractor bitMapExtractor) {
153 return contains(IndexExtractor.fromBitMapExtractor(bitMapExtractor));
154 }
155
156 @Override
157 public boolean contains(final IndexExtractor indexExtractor) {
158 return indexExtractor.processIndices(idx -> cells[idx] != 0);
159 }
160
161
162
163
164
165
166 @Override
167 public ArrayCountingBloomFilter copy() {
168 return new ArrayCountingBloomFilter(this);
169 }
170
171 @Override
172 public int getMaxCell() {
173 return Integer.MAX_VALUE;
174 }
175
176 @Override
177 public int getMaxInsert(final CellExtractor cellExtractor) {
178 final int[] max = { Integer.MAX_VALUE };
179 cellExtractor.processCells((x, y) -> {
180 final int count = cells[x] / y;
181 if (count < max[0]) {
182 max[0] = count;
183 }
184 return max[0] > 0;
185 });
186 return max[0];
187 }
188
189 @Override
190 public Shape getShape() {
191 return shape;
192 }
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211 @Override
212 public boolean isValid() {
213 return state >= 0;
214 }
215
216 @Override
217 public boolean processBitMaps(final LongPredicate consumer) {
218 Objects.requireNonNull(consumer, "consumer");
219 final int blocksm1 = BitMaps.numberOfBitMaps(cells.length) - 1;
220 int i = 0;
221 long value;
222
223 for (int j = 0; j < blocksm1; j++) {
224 value = 0;
225 for (int k = 0; k < Long.SIZE; k++) {
226 if (cells[i++] != 0) {
227 value |= BitMaps.getLongBit(k);
228 }
229 }
230 if (!consumer.test(value)) {
231 return false;
232 }
233 }
234
235 value = 0;
236 for (int k = 0; i < cells.length; k++) {
237 if (cells[i++] != 0) {
238 value |= BitMaps.getLongBit(k);
239 }
240 }
241 return consumer.test(value);
242 }
243
244 @Override
245 public boolean processCells(final CellPredicate consumer) {
246 Objects.requireNonNull(consumer, "consumer");
247 for (int i = 0; i < cells.length; i++) {
248 if (cells[i] != 0 && !consumer.test(i, cells[i])) {
249 return false;
250 }
251 }
252 return true;
253 }
254
255 @Override
256 public boolean processIndices(final IntPredicate consumer) {
257 Objects.requireNonNull(consumer, "consumer");
258 for (int i = 0; i < cells.length; i++) {
259 if (cells[i] != 0 && !consumer.test(i)) {
260 return false;
261 }
262 }
263 return true;
264 }
265
266 @Override
267 public boolean subtract(final CellExtractor other) {
268 Objects.requireNonNull(other, "other");
269 other.processCells(this::subtract);
270 return isValid();
271 }
272
273
274
275
276
277
278
279
280 private boolean subtract(final int idx, final int subtrahend) {
281 try {
282 final int updated = cells[idx] - subtrahend;
283 state |= updated;
284 cells[idx] = updated;
285 return true;
286 } catch (final IndexOutOfBoundsException e) {
287 throw new IllegalArgumentException(
288 String.format("Filter only accepts values in the [0,%d) range", getShape().getNumberOfBits()), e);
289 }
290 }
291 }