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 @Override
262 public int add(final E object, final int occurrences) {
263 throw new UnsupportedOperationException();
264 }
265
266
267
268
269 @Override
270 public void clear() {
271 final Iterator<Entry<E>> it = entrySet().iterator();
272 while (it.hasNext()) {
273 it.next();
274 it.remove();
275 }
276 }
277
278
279
280
281
282
283
284 @Override
285 public boolean contains(final Object object) {
286 return getCount(object) > 0;
287 }
288
289
290
291
292
293
294 protected Set<Entry<E>> createEntrySet() {
295 return new EntrySet<>(this);
296 }
297
298
299
300
301
302
303
304 protected abstract Iterator<Entry<E>> createEntrySetIterator();
305
306
307
308
309
310
311 protected Set<E> createUniqueSet() {
312 return new UniqueSet<>(this);
313 }
314
315
316
317
318
319
320
321 protected Iterator<E> createUniqueSetIterator() {
322 final Transformer<Entry<E>, E> transformer = Entry::getElement;
323 return IteratorUtils.transformedIterator(entrySet().iterator(), transformer);
324 }
325
326
327
328
329
330
331
332
333
334 protected void doReadObject(final ObjectInputStream in)
335 throws IOException, ClassNotFoundException {
336 final int entrySize = in.readInt();
337 for (int i = 0; i < entrySize; i++) {
338 @SuppressWarnings("unchecked")
339 final E obj = (E) in.readObject();
340 final int count = in.readInt();
341 setCount(obj, count);
342 }
343 }
344
345
346
347
348
349
350
351 protected void doWriteObject(final ObjectOutputStream out) throws IOException {
352 out.writeInt(entrySet().size());
353 for (final Entry<E> entry : entrySet()) {
354 out.writeObject(entry.getElement());
355 out.writeInt(entry.getCount());
356 }
357 }
358
359
360
361
362
363
364 @Override
365 public Set<Entry<E>> entrySet() {
366 if (entrySet == null) {
367 entrySet = createEntrySet();
368 }
369 return entrySet;
370 }
371
372 @Override
373 public boolean equals(final Object object) {
374 if (object == this) {
375 return true;
376 }
377 if (!(object instanceof MultiSet)) {
378 return false;
379 }
380 final MultiSet<?> other = (MultiSet<?>) object;
381 if (other.size() != size()) {
382 return false;
383 }
384 for (final Entry<E> entry : entrySet()) {
385 if (other.getCount(entry.getElement()) != getCount(entry.getElement())) {
386 return false;
387 }
388 }
389 return true;
390 }
391
392
393
394
395
396
397
398
399 @Override
400 public int getCount(final Object object) {
401 for (final Entry<E> entry : entrySet()) {
402 final E element = entry.getElement();
403 if (Objects.equals(element, object)) {
404 return entry.getCount();
405 }
406 }
407 return 0;
408 }
409
410 @Override
411 public int hashCode() {
412 return entrySet().hashCode();
413 }
414
415
416
417
418
419
420
421 @Override
422 public Iterator<E> iterator() {
423 return new MultiSetIterator<>(this);
424 }
425
426 @Override
427 public boolean remove(final Object object) {
428 return remove(object, 1) != 0;
429 }
430
431 @Override
432 public int remove(final Object object, final int occurrences) {
433 throw new UnsupportedOperationException();
434 }
435
436 @Override
437 public boolean removeAll(final Collection<?> coll) {
438 boolean result = false;
439 for (final Object obj : coll) {
440 final boolean changed = remove(obj, getCount(obj)) != 0;
441 result = result || changed;
442 }
443 return result;
444 }
445
446 @Override
447 public int setCount(final E object, final int count) {
448 if (count < 0) {
449 throw new IllegalArgumentException("Count must not be negative.");
450 }
451
452 final int oldCount = getCount(object);
453 if (oldCount < count) {
454 add(object, count - oldCount);
455 } else {
456 remove(object, oldCount - count);
457 }
458 return oldCount;
459 }
460
461
462
463
464
465
466 @Override
467 public int size() {
468 int totalSize = 0;
469 for (final Entry<E> entry : entrySet()) {
470 totalSize += entry.getCount();
471 }
472 return totalSize;
473 }
474
475
476
477
478
479
480 @Override
481 public String toString() {
482 return entrySet().toString();
483 }
484
485
486
487
488
489
490 protected abstract int uniqueElements();
491
492
493
494
495
496
497 @Override
498 public Set<E> uniqueSet() {
499 if (uniqueSet == null) {
500 uniqueSet = createUniqueSet();
501 }
502 return uniqueSet;
503 }
504
505 }