1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.collections4.multiset;
18
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.ObjectOutputStream;
22 import java.util.AbstractCollection;
23 import java.util.AbstractSet;
24 import java.util.Collection;
25 import java.util.Iterator;
26 import java.util.Objects;
27 import java.util.Set;
28
29 import org.apache.commons.collections4.IteratorUtils;
30 import org.apache.commons.collections4.MultiSet;
31 import org.apache.commons.collections4.Transformer;
32
33
34
35
36
37
38
39
40 public abstract class AbstractMultiSet<E> extends AbstractCollection<E> implements MultiSet<E> {
41
42
43
44
45
46
47 protected abstract static class AbstractEntry<E> implements Entry<E> {
48
49
50
51
52 public AbstractEntry() {
53
54 }
55
56 @Override
57 public boolean equals(final Object object) {
58 if (object instanceof Entry) {
59 final Entry<?> other = (Entry<?>) object;
60 final E element = getElement();
61 final Object otherElement = other.getElement();
62
63 return this.getCount() == other.getCount() &&
64 Objects.equals(element, otherElement);
65 }
66 return false;
67 }
68
69 @Override
70 public int hashCode() {
71 final E element = getElement();
72 return (element == null ? 0 : element.hashCode()) ^ getCount();
73 }
74
75 @Override
76 public String toString() {
77 return String.format("%s:%d", getElement(), getCount());
78 }
79 }
80
81
82
83
84
85
86 protected static class EntrySet<E> extends AbstractSet<Entry<E>> {
87
88 private final AbstractMultiSet<E> parent;
89
90
91
92
93
94
95 protected EntrySet(final AbstractMultiSet<E> parent) {
96 this.parent = parent;
97 }
98
99 @Override
100 public boolean contains(final Object obj) {
101 if (!(obj instanceof Entry<?>)) {
102 return false;
103 }
104 final Entry<?> entry = (Entry<?>) obj;
105 final Object element = entry.getElement();
106 return parent.getCount(element) == entry.getCount();
107 }
108
109 @Override
110 public Iterator<Entry<E>> iterator() {
111 return parent.createEntrySetIterator();
112 }
113
114 @Override
115 public boolean remove(final Object obj) {
116 if (!(obj instanceof Entry<?>)) {
117 return false;
118 }
119 final Entry<?> entry = (Entry<?>) obj;
120 final Object element = entry.getElement();
121 if (parent.contains(element)) {
122 final int count = parent.getCount(element);
123 if (entry.getCount() == count) {
124 parent.remove(element, count);
125 return true;
126 }
127 }
128 return false;
129 }
130
131 @Override
132 public int size() {
133 return parent.uniqueElements();
134 }
135 }
136
137
138
139
140 private static final class MultiSetIterator<E> implements Iterator<E> {
141 private final AbstractMultiSet<E> parent;
142 private final Iterator<Entry<E>> entryIterator;
143 private Entry<E> current;
144 private int itemCount;
145 private boolean canRemove;
146
147
148
149
150
151
152 MultiSetIterator(final AbstractMultiSet<E> parent) {
153 this.parent = parent;
154 this.entryIterator = parent.entrySet().iterator();
155 this.current = null;
156 this.canRemove = false;
157 }
158
159
160 @Override
161 public boolean hasNext() {
162 return itemCount > 0 || entryIterator.hasNext();
163 }
164
165
166 @Override
167 public E next() {
168 if (itemCount == 0) {
169 current = entryIterator.next();
170 itemCount = current.getCount();
171 }
172 canRemove = true;
173 itemCount--;
174 return current.getElement();
175 }
176
177
178 @Override
179 public void remove() {
180 if (!canRemove) {
181 throw new IllegalStateException();
182 }
183 final int count = current.getCount();
184 if (count > 1) {
185 parent.remove(current.getElement());
186 } else {
187 entryIterator.remove();
188 }
189 canRemove = false;
190 }
191 }
192
193
194
195
196
197
198 protected static class UniqueSet<E> extends AbstractSet<E> {
199
200
201 protected final AbstractMultiSet<E> parent;
202
203
204
205
206
207
208 protected UniqueSet(final AbstractMultiSet<E> parent) {
209 this.parent = parent;
210 }
211
212 @Override
213 public void clear() {
214 parent.clear();
215 }
216
217 @Override
218 public boolean contains(final Object key) {
219 return parent.contains(key);
220 }
221
222 @Override
223 public boolean containsAll(final Collection<?> coll) {
224 return parent.containsAll(coll);
225 }
226
227 @Override
228 public Iterator<E> iterator() {
229 return parent.createUniqueSetIterator();
230 }
231
232 @Override
233 public boolean remove(final Object key) {
234 return parent.remove(key, parent.getCount(key)) != 0;
235 }
236
237 @Override
238 public int size() {
239 return parent.uniqueElements();
240 }
241 }
242
243
244 private transient Set<E> uniqueSet;
245
246
247 private transient Set<Entry<E>> entrySet;
248
249
250
251
252 protected AbstractMultiSet() {
253 }
254
255 @Override
256 public boolean add(final E object) {
257 add(object, 1);
258 return true;
259 }
260
261
262
263
264
265
266
267
268 @Override
269 public int add(final E object, final int occurrences) {
270 throw new UnsupportedOperationException();
271 }
272
273
274
275
276 @Override
277 public void clear() {
278 final Iterator<Entry<E>> it = entrySet().iterator();
279 while (it.hasNext()) {
280 it.next();
281 it.remove();
282 }
283 }
284
285
286
287
288
289
290
291 @Override
292 public boolean contains(final Object object) {
293 return getCount(object) > 0;
294 }
295
296
297
298
299
300
301 protected Set<Entry<E>> createEntrySet() {
302 return new EntrySet<>(this);
303 }
304
305
306
307
308
309
310
311 protected abstract Iterator<Entry<E>> createEntrySetIterator();
312
313
314
315
316
317
318 protected Set<E> createUniqueSet() {
319 return new UniqueSet<>(this);
320 }
321
322
323
324
325
326
327
328 protected Iterator<E> createUniqueSetIterator() {
329 final Transformer<Entry<E>, E> transformer = Entry::getElement;
330 return IteratorUtils.transformedIterator(entrySet().iterator(), transformer);
331 }
332
333
334
335
336
337
338
339
340
341 protected void doReadObject(final ObjectInputStream in)
342 throws IOException, ClassNotFoundException {
343 final int entrySize = in.readInt();
344 for (int i = 0; i < entrySize; i++) {
345 @SuppressWarnings("unchecked")
346 final E obj = (E) in.readObject();
347 final int count = in.readInt();
348 setCount(obj, count);
349 }
350 }
351
352
353
354
355
356
357
358 protected void doWriteObject(final ObjectOutputStream out) throws IOException {
359 out.writeInt(entrySet().size());
360 for (final Entry<E> entry : entrySet()) {
361 out.writeObject(entry.getElement());
362 out.writeInt(entry.getCount());
363 }
364 }
365
366
367
368
369
370
371 @Override
372 public Set<Entry<E>> entrySet() {
373 if (entrySet == null) {
374 entrySet = createEntrySet();
375 }
376 return entrySet;
377 }
378
379 @Override
380 public boolean equals(final Object object) {
381 if (object == this) {
382 return true;
383 }
384 if (!(object instanceof MultiSet)) {
385 return false;
386 }
387 final MultiSet<?> other = (MultiSet<?>) object;
388 if (other.size() != size()) {
389 return false;
390 }
391 for (final Entry<E> entry : entrySet()) {
392 if (other.getCount(entry.getElement()) != getCount(entry.getElement())) {
393 return false;
394 }
395 }
396 return true;
397 }
398
399
400
401
402
403
404
405
406 @Override
407 public int getCount(final Object object) {
408 for (final Entry<E> entry : entrySet()) {
409 final E element = entry.getElement();
410 if (Objects.equals(element, object)) {
411 return entry.getCount();
412 }
413 }
414 return 0;
415 }
416
417 @Override
418 public int hashCode() {
419 return entrySet().hashCode();
420 }
421
422
423
424
425
426
427
428 @Override
429 public Iterator<E> iterator() {
430 return new MultiSetIterator<>(this);
431 }
432
433 @Override
434 public boolean remove(final Object object) {
435 return remove(object, 1) != 0;
436 }
437
438
439
440
441
442
443
444
445 @Override
446 public int remove(final Object object, final int occurrences) {
447 throw new UnsupportedOperationException();
448 }
449
450 @Override
451 public boolean removeAll(final Collection<?> coll) {
452 boolean result = false;
453 for (final Object obj : coll) {
454 final boolean changed = remove(obj, getCount(obj)) != 0;
455 result = result || changed;
456 }
457 return result;
458 }
459
460 @Override
461 public int setCount(final E object, final int count) {
462 if (count < 0) {
463 throw new IllegalArgumentException("Count must not be negative.");
464 }
465
466 final int oldCount = getCount(object);
467 if (oldCount < count) {
468 add(object, count - oldCount);
469 } else {
470 remove(object, oldCount - count);
471 }
472 return oldCount;
473 }
474
475
476
477
478
479
480 @Override
481 public int size() {
482
483 int size = 0;
484 try {
485 for (final Entry<E> entry : entrySet()) {
486 size = Math.addExact(size, entry.getCount());
487 }
488 } catch (final ArithmeticException e) {
489 size = Integer.MAX_VALUE;
490 }
491 return size;
492 }
493
494
495
496
497
498
499 @Override
500 public String toString() {
501 return entrySet().toString();
502 }
503
504
505
506
507
508
509 protected abstract int uniqueElements();
510
511
512
513
514
515
516 @Override
517 public Set<E> uniqueSet() {
518 if (uniqueSet == null) {
519 uniqueSet = createUniqueSet();
520 }
521 return uniqueSet;
522 }
523
524 }