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
51
52 public final class ArrayCountingBloomFilter implements CountingBloomFilter {
53
54
55
56
57 private final Shape shape;
58
59
60
61
62 private final int[] cells;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88 private int state;
89
90 private ArrayCountingBloomFilter(final ArrayCountingBloomFilter source) {
91 this.shape = source.shape;
92 this.state = source.state;
93 this.cells = source.cells.clone();
94 }
95
96
97
98
99
100
101 public ArrayCountingBloomFilter(final Shape shape) {
102 Objects.requireNonNull(shape, "shape");
103 this.shape = shape;
104 cells = new int[shape.getNumberOfBits()];
105 }
106
107 @Override
108 public boolean add(final CellProducer other) {
109 Objects.requireNonNull(other, "other");
110 other.forEachCell(this::add);
111 return isValid();
112 }
113
114
115
116
117
118
119
120
121 private boolean add(final int idx, final int addend) {
122 try {
123 final int updated = cells[idx] + addend;
124 state |= updated;
125 cells[idx] = updated;
126 return true;
127 } catch (final IndexOutOfBoundsException e) {
128 throw new IllegalArgumentException(
129 String.format("Filter only accepts values in the [0,%d) range", getShape().getNumberOfBits()), e);
130 }
131 }
132
133 @Override
134 public int[] asIndexArray() {
135 return IntStream.range(0, cells.length).filter(i -> cells[i] > 0).toArray();
136 }
137
138 @Override
139 public int cardinality() {
140 return (int) IntStream.range(0, cells.length).filter(i -> cells[i] > 0).count();
141 }
142
143 @Override
144 public int characteristics() {
145 return SPARSE;
146 }
147
148 @Override
149 public void clear() {
150 Arrays.fill(cells, 0);
151 }
152
153 @Override
154 public boolean contains(final BitMapProducer bitMapProducer) {
155 return contains(IndexProducer.fromBitMapProducer(bitMapProducer));
156 }
157
158 @Override
159 public boolean contains(final IndexProducer indexProducer) {
160 return indexProducer.forEachIndex(idx -> this.cells[idx] != 0);
161 }
162
163 @Override
164 public ArrayCountingBloomFilter copy() {
165 return new ArrayCountingBloomFilter(this);
166 }
167
168 @Override
169 public boolean forEachBitMap(final LongPredicate consumer) {
170 Objects.requireNonNull(consumer, "consumer");
171 final int blocksm1 = BitMap.numberOfBitMaps(cells.length) - 1;
172 int i = 0;
173 long value;
174
175 for (int j = 0; j < blocksm1; j++) {
176 value = 0;
177 for (int k = 0; k < Long.SIZE; k++) {
178 if (cells[i++] != 0) {
179 value |= BitMap.getLongBit(k);
180 }
181 }
182 if (!consumer.test(value)) {
183 return false;
184 }
185 }
186
187 value = 0;
188 for (int k = 0; i < cells.length; k++) {
189 if (cells[i++] != 0) {
190 value |= BitMap.getLongBit(k);
191 }
192 }
193 return consumer.test(value);
194 }
195
196 @Override
197 public boolean forEachCell(final CellProducer.CellConsumer consumer) {
198 Objects.requireNonNull(consumer, "consumer");
199 for (int i = 0; i < cells.length; i++) {
200 if (cells[i] != 0 && !consumer.test(i, cells[i])) {
201 return false;
202 }
203 }
204 return true;
205 }
206
207 @Override
208 public boolean forEachIndex(final IntPredicate consumer) {
209 Objects.requireNonNull(consumer, "consumer");
210 for (int i = 0; i < cells.length; i++) {
211 if (cells[i] != 0 && !consumer.test(i)) {
212 return false;
213 }
214 }
215 return true;
216 }
217
218 @Override
219 public int getMaxCell() {
220 return Integer.MAX_VALUE;
221 }
222
223 @Override
224 public int getMaxInsert(final CellProducer cellProducer) {
225 final int[] max = {Integer.MAX_VALUE};
226 cellProducer.forEachCell( (x, y) -> {
227 final int count = cells[x] / y;
228 if (count < max[0]) {
229 max[0] = count;
230 }
231 return max[0] > 0;
232 });
233 return max[0];
234 }
235
236 @Override
237 public Shape getShape() {
238 return shape;
239 }
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255 @Override
256 public boolean isValid() {
257 return state >= 0;
258 }
259
260 @Override
261 public boolean subtract(final CellProducer other) {
262 Objects.requireNonNull(other, "other");
263 other.forEachCell(this::subtract);
264 return isValid();
265 }
266
267
268
269
270
271
272
273
274 private boolean subtract(final int idx, final int subtrahend) {
275 try {
276 final int updated = cells[idx] - subtrahend;
277 state |= updated;
278 cells[idx] = updated;
279 return true;
280 } catch (final IndexOutOfBoundsException e) {
281 throw new IllegalArgumentException(
282 String.format("Filter only accepts values in the [0,%d) range", getShape().getNumberOfBits()), e);
283 }
284 }
285 }