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