1
2 package eu.simuline.util;
3
4 import eu.simuline.util.MultiSet.Multiplicity;
5
6 import java.util.Collection;
7 import java.util.Set;
8 import java.util.Map;
9 import java.util.Iterator;
10
11 /**
12 * Represents an abstract MultiSet based on a {@link Map}.
13 *
14 * <p>
15 * addAll's implementation seems strange,
16 * add seems to be buggy,
17 * Problem with overflow of multiplicities.
18 *
19 * @param <MAP>
20 * the map assigning to each element of this multi-set its multiplicity.
21 * @param <T>
22 * the class of the elements of this multi-set.
23 *
24 * Created: Sun Nov 23 23:32:06 2014
25 *
26 * @author <a href="mailto:ernst.reissner@simuline.eu">Ernst Reissner</a>
27 * @version 1.0
28 */
29 public abstract class AbstractMultiSet<MAP extends Map<T, Multiplicity>, T>
30 implements MultiSet<T> {
31
32 /* -------------------------------------------------------------------- *
33 * inner classes. *
34 * -------------------------------------------------------------------- */
35
36 /**
37 * Serves as a wrapper object for a multiplicity {@link #mult}.
38 * Unlike <code>int</code>s we have real <code>Object</code>s
39 * which can be stored in a map, e.g. {@link AbstractMultiSet#obj2mult}
40 * and unlike <code>Integer</code>s these objects are mutable.
41 */
42 // **** this implementation is not optimal:
43 // better would be immutable multiplicities
44 // or just using Integers with checks moved towards enclosing class
45 public static final class MultiplicityImpl implements Multiplicity {
46
47 /* ---------------------------------------------------------------- *
48 * fields. *
49 * ---------------------------------------------------------------- */
50
51 /**
52 * A positive integer representing a multiplicity.
53 */
54 private int mult;
55
56 /* ---------------------------------------------------------------- *
57 * constructors. *
58 * ---------------------------------------------------------------- */
59
60 /**
61 * Creates a new <code>Multiplicity</code> instance
62 * representing a <em>positive</em> multiplicity.
63 *
64 * @param mult
65 * a strictly positive <code>int</code> value
66 * representing a multiplicity.
67 * @throws IllegalArgumentException
68 * if <code>mult</code> is not strictly positive.
69 * @see #set(int)
70 */
71 private MultiplicityImpl(int mult) {
72 set(mult);
73 }
74
75 /* ---------------------------------------------------------------- *
76 * methods. *
77 * ---------------------------------------------------------------- */
78
79 protected static MultiplicityImpl create(int mult) {
80 return new MultiplicityImpl(mult);
81 }
82
83 /**
84 * Sets the multiplicity wrapped by this object
85 * to the specified value.
86 *
87 * @param mult
88 * a strictly positive <code>int</code> value
89 * representing the old multiplicity.
90 * @throws IllegalArgumentException
91 * if <code>mult</code> is not strictly positive.
92 */
93 public int set(int mult) {
94 if (mult <= 0) {
95 throw new IllegalArgumentException
96 ("Expected non-negative multiplicity; found " +
97 mult + ". ");
98 }
99 int oldMult = this.mult;
100 this.mult = mult;
101 return oldMult;
102 }
103
104 /**
105 * Adds the specified multiplicity (which may well be negative)
106 * to the wrapped multiplicity {@link #mult} which is thus modified.
107 *
108 * @param mult
109 * an <code>int</code> such that <code>this.mult + mult > 0</code>
110 * holds.
111 * @return
112 * the new multiplicity <code>this.mult + mult</code>.
113 * @throws IllegalArgumentException
114 * if <code>this.mult + mult < 0</code> holds.
115 * @throws IllegalStateException
116 * if <code>this.mult + mult == 0</code> holds.
117 * This cannot occur: if it does this is a bug within this class.
118 */
119 public int add(int mult) {
120 // **** not completely ok: overflow
121 this.mult += mult;
122 if (this.mult <= 0) {
123 if (this.mult == 0) {
124 throw new IllegalStateException
125 ("should not occur: removed element implicitely. " );
126 }
127
128 this.mult -= mult;
129 throw new IllegalArgumentException
130 ("Resulting multiplicity " +
131 this.mult + " + " + mult +
132 " should be non-negative. ");
133 }
134 return this.mult;
135 }
136
137 /**
138 * Returns the wrapped multiplicity.
139 *
140 * @return
141 * {@link #mult}.
142 */
143 public int get() {
144 return this.mult;
145 }
146
147 /**
148 * Defines the natural ordering on natural numbers.
149 *
150 * @param mult
151 * a <code>Multiplicity</code> which should in fact
152 * be another {@link Multiplicity}.
153 * @return
154 * the difference of the wrapped {@link #mult}-values.
155 * @throws NullPointerException
156 * for <code>mult == null</code>.
157 * @throws ClassCastException
158 * if <code>mult</code> is neither <code>null</code>
159 * nor an instance of {@link Multiplicity}.
160 */
161 public int compareTo(Multiplicity mult) {
162 return this.get() - mult.get();
163 }
164
165 // api-docs provided by javadoc.
166 public String toString() {
167 return "Multiplicity " + get();
168 }
169
170 /**
171 * Returns <code>true</code> if and only if
172 * <code>obj</code> is also an instance of <code>Multiplicity</code>
173 * and if the wrapped multiplicities coincide.
174 *
175 * @param obj
176 * an <code>Object</code> value
177 * which may well be <code>null</code>.
178 * @return
179 * a <code>boolean</code> value which indicates
180 * whether <code>obj</code> is also an instance
181 * of <code>Multiplicity</code>
182 * and whether the wrapped multiplicity coincides with this one.
183 * @see #compareTo
184 */
185 public boolean equals(Object obj) {
186 if (!(obj instanceof Multiplicity)) {
187 return false;
188 }
189 return ((Multiplicity) obj).get() == this.get();
190 }
191
192 // api-docs provided by javadoc.
193 public int hashCode() {
194 return this.mult;
195 }
196 } // class Multiplicity
197
198 /**
199 * A canonical implementation of {@link MultiSetIterator}
200 * defining also the methods modifying the underlying {@link MultiSet},
201 * namely {@link #remove()}, {@link #setMult(int)}
202 * and {@link #removeMult(int)}.
203 *
204 * @param <T>
205 * the class of the elements of the underlying multi-set.
206 */
207 protected static class MultiSetIteratorImpl<T>
208 implements MultiSetIterator<T> {
209
210 /* ---------------------------------------------------------------- *
211 * fields. *
212 * ---------------------------------------------------------------- */
213
214 /**
215 * An iterator on the entries
216 * of the map {@link AbstractMultiSet#obj2mult}
217 * associating each element of the underlying {@link MultiSet}
218 * with its multiplicity.
219 */
220 private final Iterator<Map.Entry<T, Multiplicity>> entrySetIter;
221
222 /**
223 * The element returned last by invoking {@link #next()}
224 * in the iterator {@link #entrySetIter}
225 * or <code>null</code> if {@link #next()} has not yet been invoked
226 * or the element returned by the last invocation of {@link #next()}
227 * has been removed in the meantime
228 * invoking a method of this iterator (instance).
229 */
230 private Map.Entry<T, Multiplicity> last;
231
232 /* ---------------------------------------------------------------- *
233 * constructors. *
234 * ---------------------------------------------------------------- */
235
236 MultiSetIteratorImpl(MultiSet<T> multiSet) {
237 this.entrySetIter = multiSet.getSetWithMults().iterator();
238 this.last = null;
239 }
240
241 /* ---------------------------------------------------------------- *
242 * methods. *
243 * ---------------------------------------------------------------- */
244
245 public final boolean hasNext() {
246 return this.entrySetIter.hasNext();
247 }
248
249 /**
250 * Returns the next element in the iteration
251 * and, as a side effect, sets {@link #last}
252 * with the mapping of that element to its current multiplicity.
253 */
254 public final T next() {
255 this.last = this.entrySetIter.next();
256 return this.last.getKey();
257 }
258
259 /**
260 * Removes from the underlying {@link MultiSet}
261 * the last element returned by {@link #next()},
262 * provided that element was not removed in the meantime
263 * and this method is supported by this iterator.
264 * As a side effect, sets {@link #last} to <code>null</code>
265 * indicating that this element has been removed.
266 *
267 * @throws UnsupportedOperationException
268 * this implementation does not throw the exception
269 * but the iterator of an immutable multi set of course do.
270 */
271 public final void remove() {
272 // throws IllegalStateException if no longer present
273 this.entrySetIter.remove();
274 this.last = null;
275 }
276
277 /**
278 * Returns the current multiplicity of the element
279 * last read by {@link #next()},
280 * provided that element was not removed in the meantime.
281 */
282 public final int getMult() {
283 // getMultObj() may throw IllegalStateException
284 return getMultObj().get();
285 }
286
287 public final Multiplicity getMultObj() {
288 if (this.last == null) {
289 // no message as for method remove()
290 throw new IllegalStateException();
291 }
292 assert this.last != null;
293 return this.last.getValue();
294 }
295 //
296 /**
297 *
298 *
299 * @throws UnsupportedOperationException
300 * this implementation does not throw the exception
301 * but the iterator of an immutable multi set of course do.
302 */
303 public final int setMult(int mult) {
304 // may throw IllegalStateException
305 Multiplicity last = getMultObj();
306 assert last != null;
307 if (mult == 0) {
308 int res = last.get();
309 // may throw UnsupportedOperationException
310 remove();
311 return res;
312 }
313 // may throw IllegalArgumentException,
314 // may throw UnsupportedOperationException
315 return last.set(mult);
316 }
317
318 /**
319 *
320 *
321 * @throws UnsupportedOperationException
322 * this implementation does not throw the exception
323 * but the iterator of an immutable multi set of course do.
324 */
325 public final int removeMult(int mult) {
326 // may throw IllegalStateException
327 Multiplicity last = getMultObj();
328 assert last != null;
329 // return value is old multiplicity
330 int oldMult = last.get();
331 if (mult == oldMult) {
332 remove();
333 return oldMult;
334 }
335
336 // may throw an IllegalArgumentException
337 // may throw UnsupportedOperationException
338 last.add(-mult);
339 return oldMult;
340 }
341
342 } // class MultiSetIteratorImpl
343
344 /* -------------------------------------------------------------------- *
345 * fields. *
346 * -------------------------------------------------------------------- */
347
348 /**
349 * Maps objects to its multiplicities.
350 * The keys are objects whereas the corresponding values
351 * are strictly positive <code>Integer</code>s
352 * representing the corresponding multiplicities.
353 * If an object is not within this set,
354 * the corresponding value is <code>null</code>.
355 * **** maybe even: object not in keyset.
356 * In the key set no <code>null</code> values may occur.
357 */
358 protected final MAP obj2mult;
359
360
361
362
363 /* -------------------------------------------------------------------- *
364 * constructors and creator methods. *
365 * -------------------------------------------------------------------- */
366
367 public AbstractMultiSet(MAP t2mult) {
368 this.obj2mult = t2mult;
369 }
370
371 /* -------------------------------------------------------------------- *
372 * methods. *
373 * -------------------------------------------------------------------- */
374
375 // Query Operations
376
377 /**
378 * Returns the number of pairwise different elements
379 * in this <code>MultiSet</code>.
380 * If this <code>MultiSet</code>
381 * contains more than <tt>Integer.MAX_VALUE</tt> elements,
382 * returns <tt>Integer.MAX_VALUE</tt>.
383 *
384 * @return
385 * the number of elements in this <code>MultiSet</code>
386 * each multiple element counted as a single one.
387 * @see #sizeWithMult()
388 */
389 public final int size() {
390 // works only, since multiplicities 0 are not allowed
391 // (null-elements in turn would still work here)
392 return this.obj2mult.size();
393 }
394
395 /**
396 * Returns the number of elements
397 * in this <code>MultiSet</code> counted with multiplicities.
398 * If this <code>MultiSet</code>
399 * contains more than <tt>Integer.MAX_VALUE</tt> elements,
400 * returns <tt>Integer.MAX_VALUE</tt>.
401 *
402 * @return
403 * the number of elements in this <code>MultiSet</code>
404 * counted with multiplicities,
405 * provided this does not exceed {@link Integer#MAX_VALUE};
406 * otherwise just {@link Integer#MAX_VALUE}.
407 * @see #size()
408 */
409 public final int sizeWithMult() {
410 int result = 0;
411 for (Multiplicity mult : this.obj2mult.values()) {
412 result += mult.get();
413 if (result < 0) {
414 return Integer.MAX_VALUE;
415 }
416 }
417 assert result >= 0;
418
419 return result;
420 }
421
422 /**
423 * Returns whether this multiple set contains no element.
424 *
425 * @return
426 * whether this multiple set contains no element.
427 */
428 public final boolean isEmpty() {
429 return this.obj2mult.isEmpty();
430 }
431
432 /**
433 * Returns a Map.Entry
434 * representing an element in this <code>MultiSet</code>
435 * with maximal multiplicity together with this multiplicity,
436 * except if this set is empty.
437 * For empty sets, <code>null</code> is returned.
438 *
439 * @return
440 * <ul>
441 * <li> if this <code>MultiSet</code> is not empty,
442 * a <code>Map.Entry</code> object <code>em</code> is returned:
443 * <code>em.getKey()</code> is an element of this <code>MultiSet</code>
444 * and <code>em.getValue()</code> is a <code>Multiplicity</code>
445 * wrapping its multiplicity <code>m = em.getValue().get()</code>.
446 * This multiplicity is maximal
447 * but if there is more than one such maximal multiplicity,
448 * it is not specified which <code>Map.Entry</code> is returned.
449 * <p>
450 * Note that <code>em.getKey()</code> may never be <code>null</code>
451 *
452 * <li> if this <code>MultiSet</code> is empty,
453 * <code>null</code> is returned.
454 * </ul>
455 * @see #getObjWithMaxMult()
456 * @see #getMaxMult()
457 */
458 private Map.Entry<T, Multiplicity> getMaxObjWithMult() {
459 // return value for empty set.
460 Map.Entry<T, Multiplicity> maxCand = null;
461
462 // search for greater value than maxVal
463 int maxVal = 0;
464 int cmpVal;
465 for (Map.Entry<T, Multiplicity> cand : this.obj2mult.entrySet()) {
466 cmpVal = cand.getValue().get();
467 if (maxVal < cmpVal) {
468 maxCand = cand;
469 maxVal = cmpVal;
470 }
471 }
472 return maxCand;
473 }
474
475 /**
476 * Returns one of the elements in this multiple set
477 * with maximal multiplicity.
478 * The return value is <code>null</code>
479 * if and only if this set is empty.
480 *
481 * @return
482 * a <code>Object o != null</code> with maximal multiplicity
483 * or <code>null</code> if this multiple set is empty.
484 * @see #isEmpty
485 */
486 public final T getObjWithMaxMult() {
487 if (isEmpty()) {
488 return null;
489 }
490 return getMaxObjWithMult().getKey();
491 }
492
493 /**
494 * Returns the maximal multiplicity of an element in this set.
495 * In particular for empty sets returns <code>0</code>.
496 *
497 * @return
498 * a non-negative <code>int</code> value
499 * which is the maximal mutliplicity of an element in this set.
500 * In particular this is <code>0</code>
501 * if and only if this set is empty.
502 */
503 public final int getMaxMult() {
504 if (isEmpty()) {
505 return 0;
506 }
507 return getMaxObjWithMult().getValue().get();
508 }
509
510 /**
511 * Returns the multiplicity
512 * with which the given object occurs within this set.
513 *
514 * @param obj
515 * an <code>Object</code> and not null.
516 * @return
517 * a non-negative <code>int</code> value
518 * which is the mutliplicity of the given element in this set.
519 * In particular this is <code>0</code> if and only if
520 * <code>obj</code> is an instance which is not in this set.
521 * @throws NullPointerException
522 * for <code>obj==null</code>.
523 * @see #setMultiplicity(Object, int)
524 * @see #getMultiplicityObj(Object)
525 */
526 public final int getMultiplicity(Object obj) {
527 // throws NullPointerException for obj==null
528 Multiplicity result = getMultiplicityObj(obj);
529 return result == null ? 0 : result.get();
530 }
531
532 /**
533 * Returns the multiplicity object of the given object in this set
534 * or <code>null</code>.
535 *
536 * @param obj
537 * an <code>Object</code> and not null.
538 * @return
539 * If <code>obj</code> is an instance which is in this set,
540 * a multiplicity object wrapping the multiplicity is returned.
541 * If <code>obj</code> is an instance which is not in this set,
542 * <code>null</code> is returned.
543 * @throws NullPointerException
544 * for <code>obj==null</code>.
545 * @see #getMultiplicity(Object)
546 */
547 public final Multiplicity getMultiplicityObj(Object obj) {
548 if (obj == null) {
549 throw new NullPointerException(); // NOPMD
550 }
551 // Here, obj != null.
552 return this.obj2mult.get(obj);
553 }
554
555 /**
556 * Returns <tt>true</tt> if this <code>MultiSet</code>
557 * contains the specified element.
558 * More formally, returns <tt>true</tt> if and only if this
559 * <code>MultiSet</code> contains at least one element <tt>e</tt>
560 * such that <tt>(o==null ? e==null : o.equals(e))</tt>.
561 *
562 * @param obj
563 * element (not <code>null</code>)
564 * whose presence in this <code>MultiSet</code> is to be tested.
565 * @return
566 * <tt>true</tt> if this <code>MultiSet</code>
567 * contains the specified element.
568 * @throws NullPointerException
569 * for <code>obj==null</code>.
570 */
571 public final boolean contains(Object obj) {
572 // throws NullPointerException for obj==null
573 return getMultiplicityObj(obj) != null;
574 }
575
576 /**
577 * Returns an iterator over the elements in this collection
578 * which emits each element exactly once,
579 * without regarding its multiplicity.
580 * <!-- There are no guarantees concerning the order
581 * in which the elements are returned
582 * (unless this collection is an instance of some class
583 * that provides a guarantee). -->
584 * For certain implementations, the iterator returned
585 * does not allow modifications of the underlying (multi-)set.
586 *
587 * @return
588 * an <tt>Iterator</tt> over the elements in this collection
589 * considering each element exactly once ignoring its multiplicity.
590 */
591 public final MultiSetIterator<T> iterator() {
592 return new MultiSetIteratorImpl<T>(this);
593 }
594
595 /**
596 * Returns an array containing all of the elements
597 * in this <code>MultiSet</code> exactly once, ignoring its multiplicity.
598 * <!--
599 * If the <code>MultiSet</code> makes any guarantees
600 * as to what order its elements are returned by its iterator,
601 * this method must return the elements in the same order.
602 * -->
603 * <p>
604 * The returned array will be "safe" in that no references to it
605 * are maintained by this <code>MultiSet</code>.
606 * (In other words, this method must allocate a new array
607 * even if this <code>MultiSet</code> is backed by an array).
608 * The caller is thus free to modify the returned array.
609 * <!-- <p>
610 * This method acts as bridge
611 * between array-based and collection-based APIs. -->
612 *
613 * @return
614 * an array containing all of the elements in this collection
615 * @see #iterator
616 */
617 public final Object[] toArray() {
618 return getSet().toArray(new Object[0]);
619 }
620
621 /**
622 * Returns an array containing all of the elements
623 * in this <code>MultiSet</code>;
624 * the runtime type of the returned array is that of the specified array.
625 * If the <code>MultiSet</code> fits in the specified array,
626 * it is returned therein.
627 * Otherwise, a new array is allocated with the runtime type
628 * of the specified array and the size of this <code>MultiSet</code>.
629 * <p>
630 * If this <code>MultiSet</code> fits in the specified array
631 * with room to spare
632 * (i.e., the array has more elements than this <code>MultiSet</code>),
633 * the elementin the array
634 * immediately following the end of the <code>MultiSet</code>
635 * is set to <tt>null</tt>.
636 * This is useful in determining the length of this
637 * <code>MultiSet</code> because this <code>MultiSet</code> does
638 * not contain any <tt>null</tt> elements.
639 * <p>
640 * <!--
641 * If this <code>MultiSet</code> makes any guarantees
642 * as to what order its elements are returned by its iterator,
643 * this method must return the elements in
644 * the same order.
645 * -->
646 * <!--
647 * <p>
648 * Like the <tt>toArray</tt> method, this method acts as bridge between
649 * array-based and collection-based APIs. Further, this method allows
650 * precise control over the runtime type of the output array, and may,
651 * under certain circumstances, be used to save allocation costs.
652 * <p>
653 * -->
654 * Suppose <tt>l</tt> is a <tt>List</tt> known to contain only strings.
655 * The following code can be used to dump the list into a newly allocated
656 * array of <tt>String</tt>:
657 *
658 * <pre>
659 * String[] x = (String[]) v.toArray(new String[0]);
660 * </pre><p>
661 *
662 * Note that <tt>toArray(new Object[0])</tt> is identical in function to
663 * <tt>toArray()</tt>.
664 *
665 * @param arr
666 * the array into which the elements of this <code>MultiSet</code>
667 * are to be stored, if it is big enough;
668 * otherwise, a new array of the same runtime type
669 * is allocated for this purpose.
670 * @return
671 * an array containing each elements of this <code>MultiSet</code>
672 * exactly once.
673 * @throws ArrayStoreException
674 * the runtime type of the specified array is not a supertype
675 * of the runtime type of every element in this <code>MultiSet</code>.
676 * @throws NullPointerException
677 * if the specified array is <tt>null</tt>.
678 */
679 public final T[] toArray(T[] arr) {
680 return getSet().toArray(arr);
681 }
682
683
684 // Modification Operations
685
686 /**
687 * Adds <code>obj</code> to this <code>MultiSet</code>
688 * and returns the new multiplicity of this object.
689 * In other words, increments the multiplicity of <code>obj</code> by one.
690 *
691 * @param obj
692 * a <code>Object</code>.
693 * Note that this object may not be <code>null</code>.
694 * @return
695 * a strictly positive <code>int</code> value:
696 * the new multiplicity of <code>obj</code>.
697 * @throws NullPointerException
698 * if the specified element is null.
699 * @throws UnsupportedOperationException
700 * if this <code>MultiSet</code> does not support this method.
701 */
702 public final int addWithMult(T obj) {
703 // throws NullPointerException for obj==null
704 return addWithMult(obj, 1);
705 }
706
707 /**
708 * Increases the multiplicity of <code>obj</code>
709 * in this <code>MultiSet</code>
710 * by the specified value <code>addMult</code>
711 * and returns the new multiplicity of this object.
712 *
713 * @param obj
714 * an <code>Object</code> instance.
715 * @param addMult
716 * a non-negative integer specifying the multiplicity
717 * with which <code>obj</code> is to be added.
718 * @return
719 * a non-negative <code>int</code> value:
720 * the new multiplicity of <code>obj</code>.
721 * @throws IllegalArgumentException
722 * for <code>addMult < 0</code>.
723 * @throws NullPointerException
724 * for <code>obj==null</code> provided <code>addMult ≥ 0</code>.
725 * @throws UnsupportedOperationException
726 * if this <code>MultiSet</code> does not support this method.
727 */
728 public final int addWithMult(T obj, int addMult) {
729
730 if (addMult < 0) {
731 throw new IllegalArgumentException
732 ("Expected non-negative multiplicity; found " +
733 addMult + ". ");
734 }
735 // throws NullPointerException for obj==null
736 Multiplicity mult = getMultiplicityObj(obj);
737
738 if (mult == null) {
739 // Here, this element is not in this set
740 if (addMult != 0) {
741 assert addMult > 0;
742 mult = MultiplicityImpl.create(addMult);
743 this.obj2mult.put(obj, mult);
744 }
745 return addMult;
746 }
747 // Here, obj is already in this set.
748 return mult.add(addMult);
749 }
750
751 /**
752 * Adds <code>obj</code> to this <code>MultiSet</code>.
753 * In other words, increments the multiplicity of <code>obj</code> by one.
754 * Returns <tt>true</tt> if this <code>MultiSet</code>
755 * interpreted as a set changed as a result of the call.
756 * (Returns <tt>false</tt> if this <code>MultiSet</code>
757 * already contains the specified element (with nontrivial multiplicity).
758 * <!--
759 * <p>
760 * <Code>MultiSet</Code>s that support this operation
761 * may place limitations on what elements may be added
762 * to this <code>MultiSet</code>.
763 * In particular, some<code>MultiSet</code>s will refuse
764 * to add <tt>null</tt> elements, and others will impose restrictions
765 * on the type of elements that may be added.
766 * <Code>MultiSet</Code> classes should clearly specify
767 * in their documentation any
768 * restrictions on what elements may be added.
769 * <p>
770 * If a <code>MultiSet</code> refuses to add a particular element
771 * for any reason other than that it already contains the element,
772 * it <i>must</i> throw an exception
773 * (rather than returning <tt>false</tt>).
774 * This preservesthe invariant
775 * that a <code>MultiSet</code> always contains the specified element
776 * after this call returns.
777 * -->
778 *
779 * @param obj
780 * element the multiplicity of which in this <code>MultiSet</code>
781 * is to be increased by one.
782 * Note that this may not be <code>null</code>.
783 * @return
784 * <tt>true</tt> if and only if
785 * the multiplicity of the specified element
786 * was <code>0</code> before the call of this method.
787 * @throws NullPointerException
788 * if the specified element is <code>null</code>.
789 * @throws UnsupportedOperationException
790 * if this <code>MultiSet</code> does not support this method.
791 */
792 public final boolean add(T obj) {
793 // throws NullPointerException for obj==null
794 Multiplicity mult = getMultiplicityObj(obj);
795 if (mult == null) {
796 mult = MultiplicityImpl.create(1);
797 this.obj2mult.put(obj, mult);
798 return true;
799 }
800 mult.add(1);
801 return false;
802 }
803
804 /**
805 * Decrements the multiplicity of <code>obj</code>
806 * in this <code>MultiSet</code> if it is present and
807 * returns the <em>old</em> multiplicity of <code>obj</code>;
808 * If this is <code>0</code> returns
809 * without altering this <code>MultiSet</code>.
810 *
811 * @param obj
812 * a <code>Object</code>.
813 * Note that this object may not be <code>null</code>.
814 * @return
815 * a non-negative <code>int</code> value:
816 * the old multiplicity of <code>obj</code>
817 * before a potential modification of this <code>MultiSet</code>.
818 * @throws NullPointerException
819 * if the specified element is null.
820 * @throws UnsupportedOperationException
821 * if this <code>MultiSet</code> does not support this method.
822 */
823 public final int removeWithMult(Object obj) {
824 // throws NullPointerException for obj==null
825 return removeWithMult(obj, 1);
826 }
827
828 /**
829 * Decreases the multiplicity of <code>obj</code>
830 * in this <code>MultiSet</code>
831 * by the specified value <code>removeMult</code> if possible
832 * and returns the <em>old</em> multiplicity of <code>obj</code>.
833 *
834 * @param obj
835 * an <code>Object</code> instance.
836 * @param removeMult
837 * a non-negative integer specifying the multiplicity
838 * with which <code>obj</code> is to be removed.
839 * @return
840 * a non-negative <code>int</code> value:
841 * the old multiplicity of <code>obj</code>
842 * before a potential modification of this <code>MultiSet</code>.
843 * @throws NullPointerException
844 * for <code>obj == null</code>.
845 * @throws IllegalArgumentException
846 * for <code>removeMult < 0</code> and also if
847 * <code>removeMult - obj.getMultiplicity() < 0</code>.
848 * @throws UnsupportedOperationException
849 * if this <code>MultiSet</code> does not support this method.
850 */
851 public final int removeWithMult(Object obj, int removeMult) {
852 if (removeMult < 0) {
853 throw new IllegalArgumentException
854 ("Expected non-negative multiplicity; found " +
855 removeMult + ". ");
856 }
857
858 // throws NullPointerException for obj==null
859 Multiplicity mult = getMultiplicityObj(obj);
860 if (mult == null) {
861 if (removeMult != 0) {
862 throw new IllegalArgumentException
863 ("Tried to remove object " + obj +
864 " which is not in this MultiSet. ");
865 }
866 return 0;
867 }
868 // return value is old multiplicity
869 int ret = mult.get();
870 if (ret == removeMult) {
871 this.obj2mult.remove(obj);
872 } else {
873 mult.add(-removeMult);
874 }
875 return ret;
876 }
877
878 /**
879 * Removes <em>all</em> instances of the specified element from this
880 * <code>MultiSet</code>, if it is present with nontrivial multiplicity.
881 * More formally,
882 * immediately after having (successively)
883 * invoked <code>s.remove(o)</code>,
884 * the condition <code>s.contains(o) == false</code> is satisfied.
885 * Returns true if this <code>MultiSet</code> contained the specified
886 * element (or equivalently, if (the underlying set of)
887 * this <code>MultiSet</code> changed as a result of the call).
888 *
889 * @param obj
890 * element the multiplicity of which in this <code>MultiSet</code>
891 * is to be increased by one.
892 * @return
893 * <tt>true</tt> if and only if this <code>MultiSet</code> changed
894 * as a result of the call.
895 * @throws NullPointerException
896 * if the specified element is <code>null</code>.
897 * @throws UnsupportedOperationException
898 * if this <code>MultiSet</code> does not support this method.
899 */
900 public final boolean remove(Object obj) {
901 if (obj == null) {
902 throw new NullPointerException(); // NOPMD
903 }
904 // Here, obj != null.
905
906 return this.obj2mult.remove(obj) != null;
907 }
908
909 /**
910 * Sets the multiplicity of <code>obj</code> to the value
911 * specified by <code>mult</code>.
912 *
913 * @param obj
914 * an <code>Object</code> instance.
915 * @param newMult
916 * a non-negative <code>int</code> value.
917 * @return
918 * the old multiplicity of <code>obj</code>
919 * as a non-negative <code>int</code> value.
920 * @throws IllegalArgumentException
921 * if either <code>obj == null</code> or <code>mult ≤ 0</code>.
922 * @throws UnsupportedOperationException
923 * if this <code>MultiSet</code> does not support this method.
924 * @see #getMultiplicity(Object)
925 */
926 public final int setMultiplicity(T obj, int newMult) {
927 if (obj == null) {
928 throw new IllegalArgumentException
929 ("Found null element. ");
930 }
931 if (newMult < 0) {
932 throw new IllegalArgumentException
933 ("Found negative multiplicity " + newMult + ". ");
934 }
935
936 Multiplicity oldMult = newMult == 0
937 ? this.obj2mult.remove(obj)
938 : this.obj2mult.put(obj, MultiplicityImpl.create(newMult));
939 return oldMult == null ? 0 : oldMult.get();
940 }
941
942 // Bulk Operations
943
944 /**
945 * Returns <tt>true</tt> if this <code>MultiSet</code>
946 * contains all of the elements in the specified collection
947 * with strictly positive multiplicity.
948 *
949 * @param coll
950 * collection to be checked for containment
951 * in this <code>MultiSet</code>.
952 * @return
953 * <tt>true</tt> if this <code>MultiSet</code>
954 * contains all of the elements in the specified collection.
955 * @throws NullPointerException
956 * if the specified collection contains one or more null elements.
957 * @throws NullPointerException
958 * if the specified collection is <tt>null</tt>.
959 * @see #contains(Object)
960 */
961 public final boolean containsAll(Collection<?> coll) {
962 for (Object cand : coll) {
963 // throws NullPointerException if cand == null
964 if (!contains(cand)) {
965 return false;
966 }
967 }
968 return true;
969 }
970
971 /**
972 * Adds <code>mvs</code> elementwise to this multi set
973 * increasing multiplicities
974 * and returns whether this caused a change
975 * of the underlying set.
976 * **** strange implementation; also: change
977 *
978 * @param mvs
979 * a <code>MultiSet</code> object.
980 * @return
981 * returns whether adding changed this <code>MultiSet</code>
982 * interpreted as a set.
983 * @throws UnsupportedOperationException
984 * if this <code>MultiSet</code> does not support this method.
985 */
986 public final boolean addAll(MultiSet<? extends T> mvs) {
987
988 int mvsMult;
989 boolean added = false;
990 for (T cand : mvs.getSet()) {
991 mvsMult = mvs.getMultiplicity(cand);
992 assert mvsMult > 0;
993 Multiplicity mult = this.obj2mult.get(cand);
994 if (mult == null) {
995 // Here, cand has not been in this multi-set
996 this.obj2mult.put(cand, MultiplicityImpl.create(mvsMult));
997 added = true;
998 } else {
999 // Here, cand has been in this multi-set
1000 mult.add(mvsMult);
1001 }
1002 } // for
1003 return added;
1004 }
1005
1006 /**
1007 * Adds <code>set</code> elementwise to this multi set
1008 * increasing multiplicities
1009 * and returns whether this caused a change
1010 * of the underlying set.
1011 * **** strange implementation; also: change
1012 *
1013 * @param set
1014 * a <code>Set</code> object.
1015 * @return
1016 * returns whether adding changed this <code>MultiSet</code>
1017 * interpreted as a set.
1018 * @throws UnsupportedOperationException
1019 * if this <code>MultiSet</code> does not support this method.
1020 */
1021 public final boolean addAll(Set<? extends T> set) {
1022 boolean added = false;
1023 for (T cand : set) {
1024 Multiplicity mult = this.obj2mult.get(cand);
1025 if (mult == null) {
1026 // Here, cand has not been in this multi-set
1027 this.obj2mult.put(cand, MultiplicityImpl.create(1));
1028 added = true;
1029 } else {
1030 // Here, cand has been in this multi-set
1031 mult.add(1);
1032 }
1033 } // for
1034 return added;
1035 }
1036
1037 /**
1038 * Removes all this <code>MultiSet</code>'s elements
1039 * that are also contained in the specified collection.
1040 * After this call returns, this <code>MultiSet</code>
1041 * will contain no elements in common with the specified collection.
1042 *
1043 * @param coll
1044 * elements to be removed from this <code>MultiSet</code>.
1045 * @return
1046 * <tt>true</tt> if this <code>MultiSet</code>
1047 * changed as a result of the call.
1048 * @throws NullPointerException
1049 * if the specified collection is <code>null</code>.
1050 * @throws UnsupportedOperationException
1051 * if this <code>MultiSet</code> does not support this method.
1052 * @see #remove(Object)
1053 * @see #contains(Object)
1054 */
1055 public final boolean removeAll(Collection<?> coll) {
1056 boolean thisChanged = false;
1057 for (Object cand : coll) {
1058 // throws NullPointerException if cand == null
1059 thisChanged |= remove(cand);
1060 }
1061 return thisChanged;
1062 }
1063
1064 /**
1065 * Retains only the elements in this <code>MultiSet</code>
1066 * that are contained in the specified collection.
1067 * In other words, removes from this <code>MultiSet</code>
1068 * all of its elements that are not contained
1069 * in the specified collection.
1070 *
1071 * @param coll
1072 * elements to be retained in this <code>MultiSet</code>.
1073 * @return
1074 * <tt>true</tt> if this <code>MultiSet</code> changed
1075 * as a result of the call.
1076 * @throws NullPointerException
1077 * if the specified collection is <tt>null</tt>.
1078 * @throws UnsupportedOperationException
1079 * if this <code>MultiSet</code> does not support this method.
1080 * @see #remove(Object)
1081 * @see #contains(Object)
1082 */
1083 public final boolean retainAll(Collection<?> coll) {
1084 boolean result = false;
1085
1086 Iterator<T> iter = iterator();
1087 T cand;
1088 while (iter.hasNext()) {
1089 cand = iter.next();
1090 if (!coll.contains(cand)) {
1091 iter.remove();
1092 result = true;
1093 }
1094 }
1095 return result;
1096 }
1097
1098 /**
1099 * Removes all of the elements from this <code>MultiSet</code>.
1100 * This <code>MultiSet</code> will be empty after this method returns.
1101 *
1102 * @throws UnsupportedOperationException
1103 * if this <code>MultiSet</code> does not support this method.
1104 */
1105 public final void clear() {
1106 this.obj2mult.clear();
1107 }
1108
1109 /**
1110 * Returns a Set view of the mapping
1111 * from the element of this <code>MultiSet</code>
1112 * to the according multiplicities.
1113 * The set is backed by the <code>MultiSet</code>,
1114 * so changes to the map are reflected in the set, and vice-versa.
1115 * If the <code>MultiSet</code> is modified
1116 * while an iteration over the set is in progress
1117 * (except through the iterator's own remove operation,
1118 * or through the setValue operation on a map entry
1119 * returned by the iterator) the results of the iteration are undefined.
1120 * The set may support element removal,
1121 * which removes the corresponding element from the <code>MultiSet</code>,
1122 * via the {@link Iterator#remove()}, {@link Set#remove(Object)},
1123 * {@link Set#removeAll(Collection)}, {@link Set#retainAll(Collection)}
1124 * and {@link #clear()} operations.
1125 * It does not support the methods
1126 * {@link #add(Object)} or {@link Set#addAll(Collection)}.
1127 */
1128 public final Set<Map.Entry<T, Multiplicity>> getSetWithMults() {
1129 return this.obj2mult.entrySet();
1130 }
1131
1132 /**
1133 * Returns <code>true</code> if and only if <code>obj</code>
1134 * is also a <code>MultiSet</code>
1135 * and contains the same elements with the same multiplicities
1136 * as this one.
1137 *
1138 * @param obj
1139 * an <code>Object</code>, possibly <code>null</code>.
1140 * @return
1141 * a <code>true</code> if and only if <code>obj</code>
1142 * is also a <code>MultiSet</code>
1143 * and contains the same elements with the same multiplicities
1144 * as this one.
1145 */
1146 public final boolean equals(Object obj) {
1147 if (!(obj instanceof MultiSet)) {
1148 return false;
1149 }
1150 MultiSet<?> other = (MultiSet) obj;
1151 return this.getSetWithMults().equals(other.getSetWithMults());
1152 }
1153
1154 public final int hashCode() {
1155 int result = 0;
1156 for (T cand : getSet()) {
1157 result += cand.hashCode() * getMultiplicity(cand);
1158 }
1159 return result;
1160 }
1161 }