1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.collections4.bag;
18
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.ObjectOutputStream;
22 import java.lang.reflect.Array;
23 import java.util.Collection;
24 import java.util.ConcurrentModificationException;
25 import java.util.Iterator;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.Objects;
29 import java.util.Set;
30
31 import org.apache.commons.collections4.Bag;
32 import org.apache.commons.collections4.CollectionUtils;
33 import org.apache.commons.collections4.set.UnmodifiableSet;
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public abstract class AbstractMapBag<E> implements Bag<E> {
48
49
50
51
52 static class BagIterator<E> implements Iterator<E> {
53 private final AbstractMapBag<E> parent;
54 private final Iterator<Map.Entry<E, MutableInteger>> entryIterator;
55 private Map.Entry<E, MutableInteger> current;
56 private int itemCount;
57 private final int mods;
58 private boolean canRemove;
59
60
61
62
63
64
65 BagIterator(final AbstractMapBag<E> parent) {
66 this.parent = parent;
67 this.entryIterator = parent.map.entrySet().iterator();
68 this.current = null;
69 this.mods = parent.modCount;
70 this.canRemove = false;
71 }
72
73
74 @Override
75 public boolean hasNext() {
76 return itemCount > 0 || entryIterator.hasNext();
77 }
78
79
80 @Override
81 public E next() {
82 if (parent.modCount != mods) {
83 throw new ConcurrentModificationException();
84 }
85 if (itemCount == 0) {
86 current = entryIterator.next();
87 itemCount = current.getValue().value;
88 }
89 canRemove = true;
90 itemCount--;
91 return current.getKey();
92 }
93
94
95 @Override
96 public void remove() {
97 if (parent.modCount != mods) {
98 throw new ConcurrentModificationException();
99 }
100 if (!canRemove) {
101 throw new IllegalStateException();
102 }
103 final MutableInteger mut = current.getValue();
104 if (mut.value > 1) {
105 mut.value--;
106 } else {
107 entryIterator.remove();
108 }
109 parent.size--;
110 canRemove = false;
111 }
112 }
113
114
115
116
117 protected static class MutableInteger {
118
119
120 protected int value;
121
122
123
124
125
126 MutableInteger(final int value) {
127 this.value = value;
128 }
129
130 @Override
131 public boolean equals(final Object obj) {
132 if (!(obj instanceof MutableInteger)) {
133 return false;
134 }
135 return ((MutableInteger) obj).value == value;
136 }
137
138 @Override
139 public int hashCode() {
140 return value;
141 }
142 }
143
144
145 private transient Map<E, MutableInteger> map;
146
147
148 private int size;
149
150
151 private transient int modCount;
152
153
154 private transient Set<E> uniqueSet;
155
156
157
158
159 protected AbstractMapBag() {
160 }
161
162
163
164
165
166
167
168 protected AbstractMapBag(final Map<E, MutableInteger> map) {
169 this.map = Objects.requireNonNull(map, "map");
170 }
171
172
173
174
175
176
177
178
179 protected AbstractMapBag(final Map<E, MutableInteger> map, final Iterable<? extends E> iterable) {
180 this(map);
181 iterable.forEach(this::add);
182 }
183
184
185
186
187
188
189
190 @Override
191 public boolean add(final E object) {
192 return add(object, 1);
193 }
194
195
196
197
198
199
200
201
202 @Override
203 public boolean add(final E object, final int nCopies) {
204 modCount++;
205 if (nCopies > 0) {
206 final MutableInteger mut = map.get(object);
207 size += nCopies;
208 if (mut == null) {
209 map.put(object, new MutableInteger(nCopies));
210 return true;
211 }
212 mut.value += nCopies;
213 }
214 return false;
215 }
216
217
218
219
220
221
222
223 @Override
224 public boolean addAll(final Collection<? extends E> coll) {
225 boolean changed = false;
226 for (final E current : coll) {
227 final boolean added = add(current);
228 changed = changed || added;
229 }
230 return changed;
231 }
232
233
234
235
236 @Override
237 public void clear() {
238 modCount++;
239 map.clear();
240 size = 0;
241 }
242
243
244
245
246
247
248
249
250 @Override
251 public boolean contains(final Object object) {
252 return map.containsKey(object);
253 }
254
255
256
257
258
259
260
261
262 boolean containsAll(final Bag<?> other) {
263 for (final Object current : other.uniqueSet()) {
264 if (getCount(current) < other.getCount(current)) {
265 return false;
266 }
267 }
268 return true;
269 }
270
271
272
273
274
275
276
277 @Override
278 public boolean containsAll(final Collection<?> coll) {
279 if (coll instanceof Bag) {
280 return containsAll((Bag<?>) coll);
281 }
282 return containsAll(new HashBag<>(coll));
283 }
284
285
286
287
288
289
290
291
292
293
294 protected void doReadObject(final Map<E, MutableInteger> map, final ObjectInputStream in)
295 throws IOException, ClassNotFoundException {
296 this.map = map;
297 final int entrySize = in.readInt();
298 for (int i = 0; i < entrySize; i++) {
299 @SuppressWarnings("unchecked")
300 final E obj = (E) in.readObject();
301 final int count = in.readInt();
302 map.put(obj, new MutableInteger(count));
303 size += count;
304 }
305 }
306
307
308
309
310
311
312
313 protected void doWriteObject(final ObjectOutputStream out) throws IOException {
314 out.writeInt(map.size());
315 for (final Entry<E, MutableInteger> entry : map.entrySet()) {
316 out.writeObject(entry.getKey());
317 out.writeInt(entry.getValue().value);
318 }
319 }
320
321
322
323
324
325
326
327
328 @Override
329 public boolean equals(final Object object) {
330 if (object == this) {
331 return true;
332 }
333 if (!(object instanceof Bag)) {
334 return false;
335 }
336 final Bag<?> other = (Bag<?>) object;
337 if (other.size() != size()) {
338 return false;
339 }
340 for (final E element : map.keySet()) {
341 if (other.getCount(element) != getCount(element)) {
342 return false;
343 }
344 }
345 return true;
346 }
347
348
349
350
351
352
353
354
355 @Override
356 public int getCount(final Object object) {
357 final MutableInteger count = map.get(object);
358 if (count != null) {
359 return count.value;
360 }
361 return 0;
362 }
363
364
365
366
367
368
369
370 protected Map<E, MutableInteger> getMap() {
371 return map;
372 }
373
374
375
376
377
378
379
380
381
382
383 @Override
384 public int hashCode() {
385 int total = 0;
386 for (final Entry<E, MutableInteger> entry : map.entrySet()) {
387 final E element = entry.getKey();
388 final MutableInteger count = entry.getValue();
389 total += (element == null ? 0 : element.hashCode()) ^ count.value;
390 }
391 return total;
392 }
393
394
395
396
397
398
399 @Override
400 public boolean isEmpty() {
401 return map.isEmpty();
402 }
403
404
405
406
407
408
409
410 @Override
411 public Iterator<E> iterator() {
412 return new BagIterator<>(this);
413 }
414
415
416
417
418
419
420
421 @Override
422 public boolean remove(final Object object) {
423 final MutableInteger mut = map.get(object);
424 if (mut == null) {
425 return false;
426 }
427 modCount++;
428 map.remove(object);
429 size -= mut.value;
430 return true;
431 }
432
433
434
435
436
437
438
439
440 @Override
441 public boolean remove(final Object object, final int nCopies) {
442 final MutableInteger mut = map.get(object);
443 if (mut == null) {
444 return false;
445 }
446 if (nCopies <= 0) {
447 return false;
448 }
449 modCount++;
450 if (nCopies < mut.value) {
451 mut.value -= nCopies;
452 size -= nCopies;
453 } else {
454 map.remove(object);
455 size -= mut.value;
456 }
457 return true;
458 }
459
460
461
462
463
464
465
466
467 @Override
468 public boolean removeAll(final Collection<?> coll) {
469 boolean result = false;
470 if (coll != null) {
471 for (final Object current : coll) {
472 final boolean changed = remove(current, 1);
473 result = result || changed;
474 }
475 }
476 return result;
477 }
478
479
480
481
482
483
484
485
486 boolean retainAll(final Bag<?> other) {
487 boolean result = false;
488 final Bag<E> excess = new HashBag<>();
489 for (final E current : uniqueSet()) {
490 final int myCount = getCount(current);
491 final int otherCount = other.getCount(current);
492 if (1 <= otherCount && otherCount <= myCount) {
493 excess.add(current, myCount - otherCount);
494 } else {
495 excess.add(current, myCount);
496 }
497 }
498 if (!excess.isEmpty()) {
499 result = removeAll(excess);
500 }
501 return result;
502 }
503
504
505
506
507
508
509
510
511 @Override
512 public boolean retainAll(final Collection<?> coll) {
513 if (coll instanceof Bag) {
514 return retainAll((Bag<?>) coll);
515 }
516 return retainAll(new HashBag<>(coll));
517 }
518
519
520
521
522
523
524 @Override
525 public int size() {
526 return size;
527 }
528
529
530
531
532
533
534 @Override
535 public Object[] toArray() {
536 final Object[] result = new Object[size()];
537 int i = 0;
538 for (final E current : map.keySet()) {
539 for (int index = getCount(current); index > 0; index--) {
540 result[i++] = current;
541 }
542 }
543 return result;
544 }
545
546
547
548
549
550
551
552
553
554
555
556
557
558 @Override
559 public <T> T[] toArray(T[] array) {
560 final int size = size();
561 if (array.length < size) {
562 @SuppressWarnings("unchecked")
563 final T[] unchecked = (T[]) Array.newInstance(array.getClass().getComponentType(), size);
564 array = unchecked;
565 }
566
567 int i = 0;
568 for (final E current : map.keySet()) {
569 for (int index = getCount(current); index > 0; index--) {
570
571 @SuppressWarnings("unchecked")
572 final T unchecked = (T) current;
573 array[i++] = unchecked;
574 }
575 }
576 while (i < array.length) {
577 array[i++] = null;
578 }
579 return array;
580 }
581
582
583
584
585
586
587 @Override
588 public String toString() {
589 if (isEmpty()) {
590 return "[]";
591 }
592 final StringBuilder buf = new StringBuilder();
593 buf.append(CollectionUtils.DEFAULT_TOSTRING_PREFIX);
594 final Iterator<E> it = uniqueSet().iterator();
595 while (it.hasNext()) {
596 final Object current = it.next();
597 final int count = getCount(current);
598 buf.append(count);
599 buf.append(CollectionUtils.COLON);
600 buf.append(current);
601 if (it.hasNext()) {
602 buf.append(CollectionUtils.COMMA);
603 }
604 }
605 buf.append(CollectionUtils.DEFAULT_TOSTRING_SUFFIX);
606 return buf.toString();
607 }
608
609
610
611
612
613
614 @Override
615 public Set<E> uniqueSet() {
616 if (uniqueSet == null) {
617 uniqueSet = UnmodifiableSet.<E>unmodifiableSet(map.keySet());
618 }
619 return uniqueSet;
620 }
621
622 }