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