AbstractMapMultiSet.java

  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.  *      http://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. import java.io.IOException;
  19. import java.io.ObjectInputStream;
  20. import java.io.ObjectOutputStream;
  21. import java.lang.reflect.Array;
  22. import java.util.ConcurrentModificationException;
  23. import java.util.Iterator;
  24. import java.util.Map;

  25. import org.apache.commons.collections4.MultiSet;
  26. import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;

  27. /**
  28.  * Abstract implementation of the {@link MultiSet} interface to simplify the
  29.  * creation of subclass implementations.
  30.  * <p>
  31.  * Subclasses specify a Map implementation to use as the internal storage. The
  32.  * map will be used to map multiset elements to a number; the number represents the
  33.  * number of occurrences of that element in the multiset.
  34.  * </p>
  35.  *
  36.  * @param <E> the type held in the multiset.
  37.  * @since 4.1
  38.  */
  39. public abstract class AbstractMapMultiSet<E> extends AbstractMultiSet<E> {

  40.     /**
  41.      * Inner class EntrySetIterator.
  42.      *
  43.      * @param <E> the element type.
  44.      */
  45.     protected static class EntrySetIterator<E> implements Iterator<Entry<E>> {

  46.         /** The parent map */
  47.         protected final AbstractMapMultiSet<E> parent;

  48.         /**
  49.          * The source Iterator.
  50.          */
  51.         protected final Iterator<Map.Entry<E, MutableInteger>> decorated;

  52.         /** The last returned entry */
  53.         protected Entry<E> last;

  54.         /** Whether remove is allowed at present */
  55.         protected boolean canRemove;

  56.         /**
  57.          * Constructs a new instance.
  58.          * @param decorated  the iterator to decorate
  59.          * @param parent  the parent multiset
  60.          */
  61.         protected EntrySetIterator(final Iterator<Map.Entry<E, MutableInteger>> decorated,
  62.                                    final AbstractMapMultiSet<E> parent) {
  63.             this.decorated = decorated;
  64.             this.parent = parent;
  65.         }

  66.         @Override
  67.         public boolean hasNext() {
  68.             return decorated.hasNext();
  69.         }

  70.         @Override
  71.         public Entry<E> next() {
  72.             last = new MultiSetEntry<>(decorated.next());
  73.             canRemove = true;
  74.             return last;
  75.         }

  76.         @Override
  77.         public void remove() {
  78.             if (!canRemove) {
  79.                 throw new IllegalStateException("Iterator remove() can only be called once after next()");
  80.             }
  81.             decorated.remove();
  82.             last = null;
  83.             canRemove = false;
  84.         }
  85.     }
  86.     /**
  87.      * Inner class iterator for the MultiSet.
  88.      */
  89.     private static final class MapBasedMultiSetIterator<E> implements Iterator<E> {
  90.         private final AbstractMapMultiSet<E> parent;
  91.         private final Iterator<Map.Entry<E, MutableInteger>> entryIterator;
  92.         private Map.Entry<E, MutableInteger> current;
  93.         private int itemCount;
  94.         private final int mods;
  95.         private boolean canRemove;

  96.         /**
  97.          * Constructs a new instance.
  98.          *
  99.          * @param parent the parent multiset
  100.          */
  101.         MapBasedMultiSetIterator(final AbstractMapMultiSet<E> parent) {
  102.             this.parent = parent;
  103.             this.entryIterator = parent.map.entrySet().iterator();
  104.             this.current = null;
  105.             this.mods = parent.modCount;
  106.             this.canRemove = false;
  107.         }

  108.         /** {@inheritDoc} */
  109.         @Override
  110.         public boolean hasNext() {
  111.             return itemCount > 0 || entryIterator.hasNext();
  112.         }

  113.         /** {@inheritDoc} */
  114.         @Override
  115.         public E next() {
  116.             if (parent.modCount != mods) {
  117.                 throw new ConcurrentModificationException();
  118.             }
  119.             if (itemCount == 0) {
  120.                 current = entryIterator.next();
  121.                 itemCount = current.getValue().value;
  122.             }
  123.             canRemove = true;
  124.             itemCount--;
  125.             return current.getKey();
  126.         }

  127.         /** {@inheritDoc} */
  128.         @Override
  129.         public void remove() {
  130.             if (parent.modCount != mods) {
  131.                 throw new ConcurrentModificationException();
  132.             }
  133.             if (!canRemove) {
  134.                 throw new IllegalStateException();
  135.             }
  136.             final MutableInteger mut = current.getValue();
  137.             if (mut.value > 1) {
  138.                 mut.value--;
  139.             } else {
  140.                 entryIterator.remove();
  141.             }
  142.             parent.size--;
  143.             canRemove = false;
  144.         }
  145.     }

  146.     /**
  147.      * Inner class MultiSetEntry.
  148.      *
  149.      * @param <E> the key type.
  150.      */
  151.     protected static class MultiSetEntry<E> extends AbstractEntry<E> {

  152.         /**
  153.          * The parent entry.
  154.          */
  155.         protected final Map.Entry<E, MutableInteger> parentEntry;

  156.         /**
  157.          * Constructs a new instance.
  158.          * @param parentEntry  the entry to decorate
  159.          */
  160.         protected MultiSetEntry(final Map.Entry<E, MutableInteger> parentEntry) {
  161.             this.parentEntry = parentEntry;
  162.         }

  163.         @Override
  164.         public int getCount() {
  165.             return parentEntry.getValue().value;
  166.         }

  167.         @Override
  168.         public E getElement() {
  169.             return parentEntry.getKey();
  170.         }
  171.     }

  172.     /**
  173.      * Mutable integer class for storing the data.
  174.      */
  175.     protected static class MutableInteger {
  176.         /** The value of this mutable. */
  177.         protected int value;

  178.         /**
  179.          * Constructs a new instance.
  180.          * @param value the initial value
  181.          */
  182.         MutableInteger(final int value) {
  183.             this.value = value;
  184.         }

  185.         @Override
  186.         public boolean equals(final Object obj) {
  187.             if (!(obj instanceof MutableInteger)) {
  188.                 return false;
  189.             }
  190.             return ((MutableInteger) obj).value == value;
  191.         }

  192.         @Override
  193.         public int hashCode() {
  194.             return value;
  195.         }
  196.     }

  197.     /**
  198.      * Inner class UniqueSetIterator.
  199.      *
  200.      * @param <E> the element type.
  201.      */
  202.     protected static class UniqueSetIterator<E> extends AbstractIteratorDecorator<E> {

  203.         /** The parent multiset */
  204.         protected final AbstractMapMultiSet<E> parent;

  205.         /** The last returned element */
  206.         protected E lastElement;

  207.         /** Whether remove is allowed at present */
  208.         protected boolean canRemove;

  209.         /**
  210.          * Constructs a new instance.
  211.          * @param iterator  the iterator to decorate
  212.          * @param parent  the parent multiset
  213.          */
  214.         protected UniqueSetIterator(final Iterator<E> iterator, final AbstractMapMultiSet<E> parent) {
  215.             super(iterator);
  216.             this.parent = parent;
  217.         }

  218.         @Override
  219.         public E next() {
  220.             lastElement = super.next();
  221.             canRemove = true;
  222.             return lastElement;
  223.         }

  224.         @Override
  225.         public void remove() {
  226.             if (!canRemove) {
  227.                 throw new IllegalStateException("Iterator remove() can only be called once after next()");
  228.             }
  229.             final int count = parent.getCount(lastElement);
  230.             super.remove();
  231.             parent.remove(lastElement, count);
  232.             lastElement = null;
  233.             canRemove = false;
  234.         }
  235.     }

  236.     /** The map to use to store the data */
  237.     private transient Map<E, MutableInteger> map;

  238.     /** The current total size of the multiset */
  239.     private transient int size;

  240.     /** The modification count for fail fast iterators */
  241.     private transient int modCount;

  242.     /**
  243.      * Constructor needed for subclass serialization.
  244.      */
  245.     protected AbstractMapMultiSet() {
  246.     }

  247.     /**
  248.      * Constructor that assigns the specified Map as the backing store. The map
  249.      * must be empty and non-null.
  250.      *
  251.      * @param map the map to assign
  252.      */
  253.     protected AbstractMapMultiSet(final Map<E, MutableInteger> map) {
  254.         this.map = map;
  255.     }

  256.     @Override
  257.     public int add(final E object, final int occurrences) {
  258.         if (occurrences < 0) {
  259.             throw new IllegalArgumentException("Occurrences must not be negative.");
  260.         }

  261.         final MutableInteger mut = map.get(object);
  262.         final int oldCount = mut != null ? mut.value : 0;

  263.         if (occurrences > 0) {
  264.             modCount++;
  265.             size += occurrences;
  266.             if (mut == null) {
  267.                 map.put(object, new MutableInteger(occurrences));
  268.             } else {
  269.                 mut.value += occurrences;
  270.             }
  271.         }
  272.         return oldCount;
  273.     }

  274.     /**
  275.      * Clears the multiset by clearing the underlying map.
  276.      */
  277.     @Override
  278.     public void clear() {
  279.         modCount++;
  280.         map.clear();
  281.         size = 0;
  282.     }

  283.     /**
  284.      * Determines if the multiset contains the given element by checking if the
  285.      * underlying map contains the element as a key.
  286.      *
  287.      * @param object the object to search for
  288.      * @return true if the multiset contains the given element
  289.      */
  290.     @Override
  291.     public boolean contains(final Object object) {
  292.         return map.containsKey(object);
  293.     }

  294.     @Override
  295.     protected Iterator<Entry<E>> createEntrySetIterator() {
  296.         return new EntrySetIterator<>(map.entrySet().iterator(), this);
  297.     }

  298.     @Override
  299.     protected Iterator<E> createUniqueSetIterator() {
  300.         return new UniqueSetIterator<>(getMap().keySet().iterator(), this);
  301.     }

  302.     /**
  303.      * Reads the multiset in using a custom routine.
  304.      *
  305.      * @param in the input stream
  306.      * @throws IOException any of the usual I/O related exceptions
  307.      * @throws ClassNotFoundException if the stream contains an object which class cannot be loaded
  308.      * @throws ClassCastException if the stream does not contain the correct objects
  309.      */
  310.     @Override
  311.     protected void doReadObject(final ObjectInputStream in)
  312.             throws IOException, ClassNotFoundException {
  313.         final int entrySize = in.readInt();
  314.         for (int i = 0; i < entrySize; i++) {
  315.             @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
  316.             final E obj = (E) in.readObject();
  317.             final int count = in.readInt();
  318.             map.put(obj, new MutableInteger(count));
  319.             size += count;
  320.         }
  321.     }

  322.     /**
  323.      * Writes the multiset out using a custom routine.
  324.      *
  325.      * @param out the output stream
  326.      * @throws IOException any of the usual I/O related exceptions
  327.      */
  328.     @Override
  329.     protected void doWriteObject(final ObjectOutputStream out) throws IOException {
  330.         out.writeInt(map.size());
  331.         for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
  332.             out.writeObject(entry.getKey());
  333.             out.writeInt(entry.getValue().value);
  334.         }
  335.     }

  336.     @Override
  337.     public boolean equals(final Object object) {
  338.         if (object == this) {
  339.             return true;
  340.         }
  341.         if (!(object instanceof MultiSet)) {
  342.             return false;
  343.         }
  344.         final MultiSet<?> other = (MultiSet<?>) object;
  345.         if (other.size() != size()) {
  346.             return false;
  347.         }
  348.         for (final E element : map.keySet()) {
  349.             if (other.getCount(element) != getCount(element)) {
  350.                 return false;
  351.             }
  352.         }
  353.         return true;
  354.     }

  355.     /**
  356.      * Gets the number of occurrence of the given element in this multiset by
  357.      * looking up its count in the underlying map.
  358.      *
  359.      * @param object the object to search for
  360.      * @return the number of occurrences of the object, zero if not found
  361.      */
  362.     @Override
  363.     public int getCount(final Object object) {
  364.         final MutableInteger count = map.get(object);
  365.         if (count != null) {
  366.             return count.value;
  367.         }
  368.         return 0;
  369.     }

  370.     /**
  371.      * Gets the map that backs this multiset.
  372.      * Not intended for interactive use outside of subclasses.
  373.      *
  374.      * @return the map being used by the MultiSet
  375.      */
  376.     protected Map<E, MutableInteger> getMap() {
  377.         return map;
  378.     }

  379.     @Override
  380.     public int hashCode() {
  381.         int total = 0;
  382.         for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
  383.             final E element = entry.getKey();
  384.             final MutableInteger count = entry.getValue();
  385.             total += (element == null ? 0 : element.hashCode()) ^ count.value;
  386.         }
  387.         return total;
  388.     }

  389.     /**
  390.      * Returns true if the underlying map is empty.
  391.      *
  392.      * @return true if multiset is empty
  393.      */
  394.     @Override
  395.     public boolean isEmpty() {
  396.         return map.isEmpty();
  397.     }

  398.     /**
  399.      * Gets an iterator over the multiset elements. Elements present in the
  400.      * MultiSet more than once will be returned repeatedly.
  401.      *
  402.      * @return the iterator
  403.      */
  404.     @Override
  405.     public Iterator<E> iterator() {
  406.         return new MapBasedMultiSetIterator<>(this);
  407.     }

  408.     @Override
  409.     public int remove(final Object object, final int occurrences) {
  410.         if (occurrences < 0) {
  411.             throw new IllegalArgumentException("Occurrences must not be negative.");
  412.         }

  413.         final MutableInteger mut = map.get(object);
  414.         if (mut == null) {
  415.             return 0;
  416.         }
  417.         final int oldCount = mut.value;
  418.         if (occurrences > 0) {
  419.             modCount++;
  420.             if (occurrences < mut.value) {
  421.                 mut.value -= occurrences;
  422.                 size -= occurrences;
  423.             } else {
  424.                 map.remove(object);
  425.                 size -= mut.value;
  426.                 mut.value = 0;
  427.             }
  428.         }
  429.         return oldCount;
  430.     }

  431.     /**
  432.      * Sets the map being wrapped.
  433.      * <p>
  434.      * <strong>Note:</strong> this method should only be used during deserialization
  435.      * </p>
  436.      *
  437.      * @param map the map to wrap
  438.      */
  439.     protected void setMap(final Map<E, MutableInteger> map) {
  440.         this.map = map;
  441.     }

  442.     /**
  443.      * Returns the number of elements in this multiset.
  444.      *
  445.      * @return current size of the multiset
  446.      */
  447.     @Override
  448.     public int size() {
  449.         return size;
  450.     }

  451.     /**
  452.      * Returns an array of all of this multiset's elements.
  453.      *
  454.      * @return an array of all of this multiset's elements
  455.      */
  456.     @Override
  457.     public Object[] toArray() {
  458.         final Object[] result = new Object[size()];
  459.         int i = 0;
  460.         for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
  461.             final E current = entry.getKey();
  462.             final MutableInteger count = entry.getValue();
  463.             for (int index = count.value; index > 0; index--) {
  464.                 result[i++] = current;
  465.             }
  466.         }
  467.         return result;
  468.     }

  469.     /**
  470.      * Returns an array of all of this multiset's elements.
  471.      * If the input array has more elements than are in the multiset,
  472.      * trailing elements will be set to null.
  473.      *
  474.      * @param <T> the type of the array elements
  475.      * @param array the array to populate
  476.      * @return an array of all of this multiset's elements
  477.      * @throws ArrayStoreException if the runtime type of the specified array is not
  478.      *   a supertype of the runtime type of the elements in this list
  479.      * @throws NullPointerException if the specified array is null
  480.      */
  481.     @Override
  482.     public <T> T[] toArray(T[] array) {
  483.         final int size = size();
  484.         if (array.length < size) {
  485.             @SuppressWarnings("unchecked") // safe as both are of type T
  486.             final T[] unchecked = (T[]) Array.newInstance(array.getClass().getComponentType(), size);
  487.             array = unchecked;
  488.         }

  489.         int i = 0;
  490.         for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
  491.             final E current = entry.getKey();
  492.             final MutableInteger count = entry.getValue();
  493.             for (int index = count.value; index > 0; index--) {
  494.                 // unsafe, will throw ArrayStoreException if types are not compatible, see Javadoc
  495.                 @SuppressWarnings("unchecked")
  496.                 final T unchecked = (T) current;
  497.                 array[i++] = unchecked;
  498.             }
  499.         }
  500.         while (i < array.length) {
  501.             array[i++] = null;
  502.         }
  503.         return array;
  504.     }

  505.     @Override
  506.     protected int uniqueElements() {
  507.         return map.size();
  508.     }
  509. }