View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.collections4.multiset;
18  
19  import java.io.IOException;
20  import java.io.InvalidObjectException;
21  import java.io.ObjectInputStream;
22  import java.io.ObjectOutputStream;
23  import java.lang.reflect.Array;
24  import java.util.ConcurrentModificationException;
25  import java.util.Iterator;
26  import java.util.Map;
27  
28  import org.apache.commons.collections4.MultiSet;
29  import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
30  
31  /**
32   * Abstract implementation of the {@link MultiSet} interface to simplify the
33   * creation of subclass implementations.
34   * <p>
35   * Subclasses specify a Map implementation to use as the internal storage. The
36   * map will be used to map multiset elements to a number; the number represents the
37   * number of occurrences of that element in the multiset.
38   * </p>
39   *
40   * @param <E> The type held in the multiset.
41   * @since 4.1
42   */
43  public abstract class AbstractMapMultiSet<E> extends AbstractMultiSet<E> {
44  
45      /**
46       * Inner class EntrySetIterator.
47       *
48       * @param <E> The element type.
49       */
50      protected static class EntrySetIterator<E> implements Iterator<Entry<E>> {
51  
52          /** The parent map */
53          protected final AbstractMapMultiSet<E> parent;
54  
55          /**
56           * The source Iterator.
57           */
58          protected final Iterator<Map.Entry<E, MutableInteger>> decorated;
59  
60          /** The last returned entry. */
61          protected Entry<E> last;
62  
63          /** Whether remove is allowed at present. */
64          protected boolean canRemove;
65  
66          /**
67           * Constructs a new instance.
68           *
69           * @param decorated  The iterator to decorate.
70           * @param parent  The parent multiset.
71           */
72          protected EntrySetIterator(final Iterator<Map.Entry<E, MutableInteger>> decorated,
73                                     final AbstractMapMultiSet<E> parent) {
74              this.decorated = decorated;
75              this.parent = parent;
76          }
77  
78          @Override
79          public boolean hasNext() {
80              return decorated.hasNext();
81          }
82  
83          @Override
84          public Entry<E> next() {
85              last = new MultiSetEntry<>(decorated.next());
86              canRemove = true;
87              return last;
88          }
89  
90          @Override
91          public void remove() {
92              if (!canRemove) {
93                  throw new IllegalStateException("Iterator remove() can only be called once after next()");
94              }
95              final int count = last.getCount();
96              decorated.remove();
97              parent.size -= count;
98              parent.modCount++;
99              last = null;
100             canRemove = false;
101         }
102     }
103 
104     /**
105      * Inner class iterator for the MultiSet.
106      */
107     private static final class MapBasedMultiSetIterator<E> implements Iterator<E> {
108         private final AbstractMapMultiSet<E> parent;
109         private final Iterator<Map.Entry<E, MutableInteger>> entryIterator;
110         private Map.Entry<E, MutableInteger> current;
111         private int itemCount;
112         private final int mods;
113         private boolean canRemove;
114 
115         /**
116          * Constructs a new instance.
117          *
118          * @param parent The parent multiset.
119          */
120         MapBasedMultiSetIterator(final AbstractMapMultiSet<E> parent) {
121             this.parent = parent;
122             this.entryIterator = parent.map.entrySet().iterator();
123             this.current = null;
124             this.mods = parent.modCount;
125             this.canRemove = false;
126         }
127 
128         /** {@inheritDoc} */
129         @Override
130         public boolean hasNext() {
131             return itemCount > 0 || entryIterator.hasNext();
132         }
133 
134         /** {@inheritDoc} */
135         @Override
136         public E next() {
137             if (parent.modCount != mods) {
138                 throw new ConcurrentModificationException();
139             }
140             if (itemCount == 0) {
141                 current = entryIterator.next();
142                 itemCount = current.getValue().value;
143             }
144             canRemove = true;
145             itemCount--;
146             return current.getKey();
147         }
148 
149         /** {@inheritDoc} */
150         @Override
151         public void remove() {
152             if (parent.modCount != mods) {
153                 throw new ConcurrentModificationException();
154             }
155             if (!canRemove) {
156                 throw new IllegalStateException();
157             }
158             final MutableInteger mut = current.getValue();
159             if (mut.value > 1) {
160                 mut.value--;
161             } else {
162                 entryIterator.remove();
163             }
164             parent.size--;
165             canRemove = false;
166         }
167     }
168 
169     /**
170      * Inner class MultiSetEntry.
171      *
172      * @param <E> The key type.
173      */
174     protected static class MultiSetEntry<E> extends AbstractEntry<E> {
175 
176         /**
177          * The parent entry.
178          */
179         protected final Map.Entry<E, MutableInteger> parentEntry;
180 
181         /**
182          * Constructs a new instance.
183          *
184          * @param parentEntry  The entry to decorate.
185          */
186         protected MultiSetEntry(final Map.Entry<E, MutableInteger> parentEntry) {
187             this.parentEntry = parentEntry;
188         }
189 
190         @Override
191         public int getCount() {
192             return parentEntry.getValue().value;
193         }
194 
195         @Override
196         public E getElement() {
197             return parentEntry.getKey();
198         }
199     }
200 
201     /**
202      * Mutable integer class for storing the data.
203      */
204     protected static class MutableInteger {
205 
206         /** The value of this mutable. */
207         protected int value;
208 
209         /**
210          * Constructs a new instance.
211          *
212          * @param value The initial value.
213          */
214         MutableInteger(final int value) {
215             this.value = value;
216         }
217 
218         @Override
219         public boolean equals(final Object obj) {
220             if (!(obj instanceof MutableInteger)) {
221                 return false;
222             }
223             return ((MutableInteger) obj).value == value;
224         }
225 
226         @Override
227         public int hashCode() {
228             return value;
229         }
230     }
231 
232     /**
233      * Inner class UniqueSetIterator.
234      *
235      * @param <E> The element type.
236      */
237     protected static class UniqueSetIterator<E> extends AbstractIteratorDecorator<E> {
238 
239         /** The parent multiset. */
240         protected final AbstractMapMultiSet<E> parent;
241 
242         /** The last returned element. */
243         protected E lastElement;
244 
245         /** Whether remove is allowed at present. */
246         protected boolean canRemove;
247 
248         /**
249          * Constructs a new instance.
250          *
251          * @param iterator  The iterator to decorate.
252          * @param parent  The parent multiset.
253          */
254         protected UniqueSetIterator(final Iterator<E> iterator, final AbstractMapMultiSet<E> parent) {
255             super(iterator);
256             this.parent = parent;
257         }
258 
259         @Override
260         public E next() {
261             lastElement = super.next();
262             canRemove = true;
263             return lastElement;
264         }
265 
266         @Override
267         public void remove() {
268             if (!canRemove) {
269                 throw new IllegalStateException("Iterator remove() can only be called once after next()");
270             }
271             final int count = parent.getCount(lastElement);
272             super.remove();
273             parent.size -= count;
274             parent.modCount++;
275             lastElement = null;
276             canRemove = false;
277         }
278     }
279 
280     /** The map to use to store the data. */
281     private transient Map<E, MutableInteger> map;
282 
283     /** The current total size of the multiset; kept exact past {@link Integer#MAX_VALUE}, {@link #size()} saturates */
284     private transient long size;
285 
286     /** The modification count for fail fast iterators. */
287     private transient int modCount;
288 
289     /**
290      * Constructor needed for subclass serialization.
291      */
292     protected AbstractMapMultiSet() {
293     }
294 
295     /**
296      * Constructor that assigns the specified Map as the backing store. The map
297      * must be empty and non-null.
298      *
299      * @param map The map to assign.
300      */
301     protected AbstractMapMultiSet(final Map<E, MutableInteger> map) {
302         this.map = map;
303     }
304 
305     /**
306      * Constructs a new instance that assigns the specified Map as the backing store. The map
307      * must be empty and non-null. The multiset is filled from the iterable elements.
308      *
309      * @param map The map to assign.
310      * @param iterable The iterable of elements to add.
311      * @since 4.6.0
312      */
313     protected AbstractMapMultiSet(final Map<E, MutableInteger> map, final Iterable<? extends E> iterable) {
314         this(map);
315         iterable.forEach(this::add);
316     }
317 
318     @Override
319     public int add(final E object, final int occurrences) {
320         if (occurrences < 0) {
321             throw new IllegalArgumentException("Occurrences must not be negative.");
322         }
323 
324         final MutableInteger mut = map.get(object);
325         final int oldCount = mut != null ? mut.value : 0;
326 
327         if (occurrences > 0) {
328             modCount++;
329             if (mut == null) {
330                 map.put(object, new MutableInteger(occurrences));
331                 size += occurrences;
332             } else {
333                 final int applied = Math.min(occurrences, Integer.MAX_VALUE - mut.value);
334                 mut.value += applied;
335                 size += applied;
336             }
337         }
338         return oldCount;
339     }
340 
341     /**
342      * Clears the multiset by clearing the underlying map.
343      */
344     @Override
345     public void clear() {
346         modCount++;
347         map.clear();
348         size = 0;
349     }
350 
351     /**
352      * Determines if the multiset contains the given element by checking if the
353      * underlying map contains the element as a key.
354      *
355      * @param object The object to search for.
356      * @return true if the multiset contains the given element.
357      */
358     @Override
359     public boolean contains(final Object object) {
360         return map.containsKey(object);
361     }
362 
363     @Override
364     protected Iterator<Entry<E>> createEntrySetIterator() {
365         return new EntrySetIterator<>(map.entrySet().iterator(), this);
366     }
367 
368     @Override
369     protected Iterator<E> createUniqueSetIterator() {
370         return new UniqueSetIterator<>(getMap().keySet().iterator(), this);
371     }
372 
373     /**
374      * Reads the multiset in using a custom routine.
375      *
376      * @param in The input stream.
377      * @throws IOException any of the usual I/O related exceptions.
378      * @throws ClassNotFoundException if the stream contains an object which class cannot be loaded.
379      * @throws ClassCastException if the stream does not contain the correct objects.
380      */
381     @Override
382     protected void doReadObject(final ObjectInputStream in)
383             throws IOException, ClassNotFoundException {
384         final int entrySize = in.readInt();
385         for (int i = 0; i < entrySize; i++) {
386             @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
387             final E obj = (E) in.readObject();
388             final int count = in.readInt();
389             if (count < 1) {
390                 throw new InvalidObjectException("Invalid count for entry: " + count);
391             }
392             map.put(obj, new MutableInteger(count));
393             size += count;
394         }
395     }
396 
397     /**
398      * Writes the multiset out using a custom routine.
399      *
400      * @param out The output stream.
401      * @throws IOException any of the usual I/O related exceptions.
402      */
403     @Override
404     protected void doWriteObject(final ObjectOutputStream out) throws IOException {
405         out.writeInt(map.size());
406         for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
407             out.writeObject(entry.getKey());
408             out.writeInt(entry.getValue().value);
409         }
410     }
411 
412     @Override
413     public boolean equals(final Object object) {
414         if (object == this) {
415             return true;
416         }
417         if (!(object instanceof MultiSet)) {
418             return false;
419         }
420         final MultiSet<?> other = (MultiSet<?>) object;
421         if (other.size() != size()) {
422             return false;
423         }
424         for (final E element : map.keySet()) {
425             if (other.getCount(element) != getCount(element)) {
426                 return false;
427             }
428         }
429         return true;
430     }
431 
432     /**
433      * Gets the number of occurrence of the given element in this multiset by
434      * looking up its count in the underlying map.
435      *
436      * @param object The object to search for.
437      * @return The number of occurrences of the object, zero if not found.
438      */
439     @Override
440     public int getCount(final Object object) {
441         final MutableInteger count = map.get(object);
442         if (count != null) {
443             return count.value;
444         }
445         return 0;
446     }
447 
448     /**
449      * Gets the map that backs this multiset.
450      * Not intended for interactive use outside of subclasses.
451      *
452      * @return The map being used by the MultiSet.
453      */
454     protected Map<E, MutableInteger> getMap() {
455         return map;
456     }
457 
458     @Override
459     public int hashCode() {
460         int total = 0;
461         for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
462             final E element = entry.getKey();
463             final MutableInteger count = entry.getValue();
464             total += (element == null ? 0 : element.hashCode()) ^ count.value;
465         }
466         return total;
467     }
468 
469     /**
470      * Returns true if the underlying map is empty.
471      *
472      * @return true if multiset is empty.
473      */
474     @Override
475     public boolean isEmpty() {
476         return map.isEmpty();
477     }
478 
479     /**
480      * Gets an iterator over the multiset elements. Elements present in the
481      * MultiSet more than once will be returned repeatedly.
482      *
483      * @return The iterator.
484      */
485     @Override
486     public Iterator<E> iterator() {
487         return new MapBasedMultiSetIterator<>(this);
488     }
489 
490     @Override
491     public int remove(final Object object, final int occurrences) {
492         if (occurrences < 0) {
493             throw new IllegalArgumentException("Occurrences must not be negative.");
494         }
495 
496         final MutableInteger mut = map.get(object);
497         if (mut == null) {
498             return 0;
499         }
500         final int oldCount = mut.value;
501         if (occurrences > 0) {
502             modCount++;
503             if (occurrences < mut.value) {
504                 mut.value -= occurrences;
505                 size -= occurrences;
506             } else {
507                 map.remove(object);
508                 size -= mut.value;
509                 mut.value = 0;
510             }
511         }
512         return oldCount;
513     }
514 
515     /**
516      * Sets the map being wrapped.
517      * <p>
518      * <strong>Note:</strong> this method should only be used during deserialization
519      * </p>
520      *
521      * @param map The map to wrap.
522      */
523     protected void setMap(final Map<E, MutableInteger> map) {
524         this.map = map;
525     }
526 
527     /**
528      * Returns the number of elements in this multiset, or {@code Integer.MAX_VALUE}
529      * if the multiset contains more than {@code Integer.MAX_VALUE} elements.
530      *
531      * @return current size of the multiset.
532      */
533     @Override
534     public int size() {
535         return (int) Math.min(size, Integer.MAX_VALUE);
536     }
537 
538     /**
539      * Returns an array of all of this multiset's elements.
540      *
541      * @return An array of all of this multiset's elements.
542      */
543     @Override
544     public Object[] toArray() {
545         final Object[] result = new Object[size()];
546         int i = 0;
547         for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
548             final E current = entry.getKey();
549             final MutableInteger count = entry.getValue();
550             for (int index = count.value; index > 0; index--) {
551                 result[i++] = current;
552             }
553         }
554         return result;
555     }
556 
557     /**
558      * Returns an array of all of this multiset's elements. If the input array has more elements than are in the multiset, trailing elements will be set to
559      * null.
560      *
561      * @param <T>   The type of the array elements.
562      * @param array The array to populate.
563      * @return An array of all of this multiset's elements.
564      * @throws ArrayStoreException  if the runtime type of the specified array is not a supertype of the runtime type of the elements in this list.
565      * @throws NullPointerException if the specified array is null.
566      */
567     @Override
568     public <T> T[] toArray(T[] array) {
569         final int size = size();
570         if (array.length < size) {
571             @SuppressWarnings("unchecked") // safe as both are of type T
572             final T[] unchecked = (T[]) Array.newInstance(array.getClass().getComponentType(), size);
573             array = unchecked;
574         }
575 
576         int i = 0;
577         for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
578             final E current = entry.getKey();
579             final MutableInteger count = entry.getValue();
580             for (int index = count.value; index > 0; index--) {
581                 // unsafe, will throw ArrayStoreException if types are not compatible, see Javadoc
582                 @SuppressWarnings("unchecked")
583                 final T unchecked = (T) current;
584                 array[i++] = unchecked;
585             }
586         }
587         while (i < array.length) {
588             array[i++] = null;
589         }
590         return array;
591     }
592 
593     @Override
594     protected int uniqueElements() {
595         return map.size();
596     }
597 }