You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by tn...@apache.org on 2015/11/21 21:13:35 UTC

svn commit: r1715563 - in /commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset: AbstractMapMultiSet.java AbstractMultiSet.java HashMultiSet.java

Author: tn
Date: Sat Nov 21 20:13:35 2015
New Revision: 1715563

URL: http://svn.apache.org/viewvc?rev=1715563&view=rev
Log:
Add abstract class to simplify creation of MultiSet implementations, needed for MultiValuedMap; additional cleanup.

Added:
    commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java   (with props)
Modified:
    commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMapMultiSet.java
    commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/HashMultiSet.java

Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMapMultiSet.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMapMultiSet.java?rev=1715563&r1=1715562&r2=1715563&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMapMultiSet.java (original)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMapMultiSet.java Sat Nov 21 20:13:35 2015
@@ -20,16 +20,11 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.lang.reflect.Array;
-import java.util.AbstractCollection;
-import java.util.AbstractSet;
-import java.util.Collection;
 import java.util.ConcurrentModificationException;
 import java.util.Iterator;
 import java.util.Map;
-import java.util.Set;
 
 import org.apache.commons.collections4.MultiSet;
-import org.apache.commons.collections4.collection.AbstractCollectionDecorator;
 import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
 
 /**
@@ -43,7 +38,7 @@ import org.apache.commons.collections4.i
  * @since 4.1
  * @version $Id$
  */
-public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> implements MultiSet<E> {
+public abstract class AbstractMapMultiSet<E> extends AbstractMultiSet<E> {
 
     /** The map to use to store the data */
     private transient Map<E, MutableInteger> map;
@@ -51,10 +46,6 @@ public abstract class AbstractMapMultiSe
     private transient int size;
     /** The modification count for fail fast iterators */
     private transient int modCount;
-    /** View of the elements */
-    private transient Set<E> uniqueSet;
-    /** View of the entries */
-    private transient Set<Entry<E>> entrySet;
 
     /**
      * Constructor needed for subclass serialisation.
@@ -78,12 +69,23 @@ public abstract class AbstractMapMultiSe
      * Utility method for implementations to access the map that backs this multiset.
      * Not intended for interactive use outside of subclasses.
      *
-     * @return the map being used by the Bag
+     * @return the map being used by the MultiSet
      */
     protected Map<E, MutableInteger> getMap() {
         return map;
     }
 
+    /**
+     * Sets the map being wrapped.
+     * <p>
+     * <b>NOTE:</b> this method should only be used during deserialization
+     *
+     * @param map the map to wrap
+     */
+    protected void setMap(Map<E, MutableInteger> map) {
+        this.map = map;
+    }
+
     //-----------------------------------------------------------------------
     /**
      * Returns the number of elements in this multiset.
@@ -121,21 +123,6 @@ public abstract class AbstractMapMultiSe
         return 0;
     }
 
-    @Override
-    public int setCount(final E object, final int count) {
-        if (count < 0) {
-            throw new IllegalArgumentException("Count must not be negative.");
-        }
-
-        int oldCount = getCount(object);
-        if (oldCount < count) {
-            add(object, count - oldCount);
-        } else {
-            remove(object, oldCount - count);
-        }
-        return oldCount;
-    }
-
     //-----------------------------------------------------------------------
     /**
      * Determines if the multiset contains the given element by checking if the
@@ -158,13 +145,13 @@ public abstract class AbstractMapMultiSe
      */
     @Override
     public Iterator<E> iterator() {
-        return new MultiSetIterator<E>(this);
+        return new MapBasedMultiSetIterator<E>(this);
     }
 
     /**
      * Inner class iterator for the MultiSet.
      */
-    static class MultiSetIterator<E> implements Iterator<E> {
+    private static class MapBasedMultiSetIterator<E> implements Iterator<E> {
         private final AbstractMapMultiSet<E> parent;
         private final Iterator<Map.Entry<E, MutableInteger>> entryIterator;
         private Map.Entry<E, MutableInteger> current;
@@ -177,7 +164,7 @@ public abstract class AbstractMapMultiSe
          *
          * @param parent the parent multiset
          */
-        public MultiSetIterator(final AbstractMapMultiSet<E> parent) {
+        public MapBasedMultiSetIterator(final AbstractMapMultiSet<E> parent) {
             this.parent = parent;
             this.entryIterator = parent.map.entrySet().iterator();
             this.current = null;
@@ -228,12 +215,6 @@ public abstract class AbstractMapMultiSe
 
     //-----------------------------------------------------------------------
     @Override
-    public boolean add(final E object) {
-        add(object, 1);
-        return true;
-    }
-
-    @Override
     public int add(final E object, final int occurrences) {
         if (occurrences < 0) {
             throw new IllegalArgumentException("Occurrences must not be negative.");
@@ -266,11 +247,6 @@ public abstract class AbstractMapMultiSe
     }
 
     @Override
-    public boolean remove(final Object object) {
-        return remove(object, 1) != 0;
-    }
-
-    @Override
     public int remove(final Object object, final int occurrences) {
         if (occurrences < 0) {
             throw new IllegalArgumentException("Occurrences must not be negative.");
@@ -294,18 +270,6 @@ public abstract class AbstractMapMultiSe
         return oldCount;
     }
 
-    @Override
-    public boolean removeAll(final Collection<?> coll) {
-        boolean result = false;
-        final Iterator<?> i = coll.iterator();
-        while (i.hasNext()) {
-            final Object obj = i.next();
-            final boolean changed = remove(obj, getCount(obj)) != 0;
-            result = result || changed;
-        }
-        return result;
-    }
-
     //-----------------------------------------------------------------------
     /**
      * Mutable integer class for storing the data.
@@ -337,165 +301,23 @@ public abstract class AbstractMapMultiSe
     }
 
     //-----------------------------------------------------------------------
-    /**
-     * Returns an array of all of this multiset's elements.
-     *
-     * @return an array of all of this multiset's elements
-     */
-    @Override
-    public Object[] toArray() {
-        final Object[] result = new Object[size()];
-        int i = 0;
-        final Iterator<E> it = map.keySet().iterator();
-        while (it.hasNext()) {
-            final E current = it.next();
-            for (int index = getCount(current); index > 0; index--) {
-                result[i++] = current;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * 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 null.
-     *
-     * @param <T> the type of the array elements
-     * @param array the array to populate
-     * @return an array of all of this multiset's elements
-     * @throws ArrayStoreException if the runtime type of the specified array is not
-     *   a supertype of the runtime type of the elements in this list
-     * @throws NullPointerException if the specified array is null
-     */
     @Override
-    public <T> T[] toArray(T[] array) {
-        final int size = size();
-        if (array.length < size) {
-            @SuppressWarnings("unchecked") // safe as both are of type T
-            final T[] unchecked = (T[]) Array.newInstance(array.getClass().getComponentType(), size);
-            array = unchecked;
-        }
-
-        int i = 0;
-        final Iterator<E> it = map.keySet().iterator();
-        while (it.hasNext()) {
-            final E current = it.next();
-            for (int index = getCount(current); index > 0; index--) {
-                // unsafe, will throw ArrayStoreException if types are not compatible, see javadoc
-                @SuppressWarnings("unchecked")
-                final T unchecked = (T) current;
-                array[i++] = unchecked;
-            }
-        }
-        while (i < array.length) {
-            array[i++] = null;
-        }
-        return array;
+    protected Iterator<E> createUniqueSetIterator() {
+        return new UniqueSetIterator<E>(getMap().keySet().iterator(), this);
     }
 
-    /**
-     * Returns a view of the underlying map's key set.
-     *
-     * @return the set of unique elements in this multiset
-     */
     @Override
-    public Set<E> uniqueSet() {
-        if (uniqueSet == null) {
-            uniqueSet = new UniqueSet<E>(this);
-        }
-        return uniqueSet;
-    }
-
-    /**
-     * Creates a unique set iterator.
-     * Subclasses can override this to return iterators with different properties.
-     *
-     * @param iterator  the iterator to decorate
-     * @return the uniqueSet iterator
-     */
-    protected Iterator<E> createUniqueSetIterator(final Iterator<E> iterator) {
-        return new UniqueSetIterator<E>(iterator, this);
+    protected int uniqueElements() {
+        return map.size();
     }
 
-    /**
-     * Returns an unmodifiable view of the underlying map's key set.
-     *
-     * @return the set of unique elements in this multiset
-     */
     @Override
-    public Set<Entry<E>> entrySet() {
-        if (entrySet == null) {
-            entrySet = new EntrySet<E>(this);
-        }
-        return entrySet;
-    }
-
-    /**
-     * Creates an entry set iterator.
-     * Subclasses can override this to return iterators with different properties.
-     *
-     * @param iterator  the iterator to decorate
-     * @return the entrySet iterator
-     */
-    protected Iterator<Entry<E>> createEntrySetIterator(final Iterator<Map.Entry<E, MutableInteger>> iterator) {
-        return new EntrySetIterator<E>(iterator, this);
+    protected Iterator<Entry<E>> createEntrySetIterator() {
+        return new EntrySetIterator<E>(map.entrySet().iterator(), this);
     }
 
     //-----------------------------------------------------------------------
     /**
-     * Inner class UniqueSet.
-     */
-    protected static class UniqueSet<E> extends AbstractCollectionDecorator<E> implements Set<E> {
-
-        /** Serialization version */
-        private static final long serialVersionUID = 20150610L;
-
-        /** The parent multiset */
-        protected final AbstractMapMultiSet<E> parent;
-
-        /**
-         * Constructs a new unique element view of the MultiSet.
-         *
-         * @param parent  the parent MultiSet
-         */
-        protected UniqueSet(final AbstractMapMultiSet<E> parent) {
-            super(parent.map.keySet());
-            this.parent = parent;
-        }
-
-        @Override
-        public Iterator<E> iterator() {
-            return parent.createUniqueSetIterator(super.iterator());
-        }
-
-        @Override
-        public boolean contains(final Object key) {
-            return parent.contains(key);
-        }
-
-        @Override
-        public boolean remove(final Object key) {
-            return parent.remove(key, parent.getCount(key)) != 0;
-        }
-
-        @Override
-        public boolean equals(final Object object) {
-            return object == this || decorated().equals(object);
-        }
-
-        @Override
-        public int hashCode() {
-            return decorated().hashCode();
-        }
-
-        @Override
-        public void clear() {
-            parent.clear();
-        }
-    }
-
-    /**
      * Inner class UniqueSetIterator.
      */
     protected static class UniqueSetIterator<E> extends AbstractIteratorDecorator<E> {
@@ -539,61 +361,6 @@ public abstract class AbstractMapMultiSe
         }
     }
 
-    //-----------------------------------------------------------------------
-    /**
-     * Inner class EntrySet.
-     */
-    protected static class EntrySet<E> extends AbstractSet<Entry<E>> {
-
-        private final AbstractMapMultiSet<E> parent;
-
-        /**
-         * Constructs a new view of the MultiSet.
-         *
-         * @param parent  the parent MultiSet
-         */
-        protected EntrySet(final AbstractMapMultiSet<E> parent) {
-            this.parent = parent;
-        }
-
-        @Override
-        public int size() {
-            return parent.map.entrySet().size();
-        }
-
-        @Override
-        public Iterator<Entry<E>> iterator() {
-            return parent.createEntrySetIterator(parent.map.entrySet().iterator());
-        }
-
-        @Override
-        public boolean contains(final Object obj) {
-            if (obj instanceof Entry<?> == false) {
-                return false;
-            }
-            final Entry<?> entry = (Entry<?>) obj;
-            final Object element = entry.getElement();
-            return parent.getCount(element) == entry.getCount();
-        }
-
-        @Override
-        public boolean remove(final Object obj) {
-            if (obj instanceof Entry<?> == false) {
-                return false;
-            }
-            final Entry<?> entry = (Entry<?>) obj;
-            final Object element = entry.getElement();
-            if (parent.contains(element)) {
-                final int count = parent.getCount(element);
-                if (entry.getCount() == count) {
-                    parent.remove(element, count);
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-
     /**
      * Inner class EntrySetIterator.
      */
@@ -647,7 +414,7 @@ public abstract class AbstractMapMultiSe
     /**
      * Inner class MultiSetEntry.
      */
-    protected static class MultiSetEntry<E> implements Entry<E> {
+    protected static class MultiSetEntry<E> extends AbstractEntry<E> {
 
         protected final Map.Entry<E, MutableInteger> parentEntry;
 
@@ -668,20 +435,15 @@ public abstract class AbstractMapMultiSe
         public int getCount() {
             return parentEntry.getValue().value;
         }
-
-        @Override
-        public String toString() {
-            return String.format("%s:%d", getElement(), getCount());
-        }
-
     }
 
     //-----------------------------------------------------------------------
     /**
-     * Write the map out using a custom routine.
+     * Write the multiset out using a custom routine.
      * @param out the output stream
      * @throws IOException any of the usual I/O related exceptions
      */
+    @Override
     protected void doWriteObject(final ObjectOutputStream out) throws IOException {
         out.writeInt(map.size());
         for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
@@ -691,16 +453,15 @@ public abstract class AbstractMapMultiSe
     }
 
     /**
-     * Read the map in using a custom routine.
-     * @param map the map to use
+     * Read the multiset in using a custom routine.
      * @param in the input stream
      * @throws IOException any of the usual I/O related exceptions
      * @throws ClassNotFoundException if the stream contains an object which class can not be loaded
      * @throws ClassCastException if the stream does not contain the correct objects
      */
-    protected void doReadObject(final Map<E, MutableInteger> map, final ObjectInputStream in)
+    @Override
+    protected void doReadObject(final ObjectInputStream in)
             throws IOException, ClassNotFoundException {
-        this.map = map;
         final int entrySize = in.readInt();
         for (int i = 0; i < entrySize; i++) {
             @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
@@ -712,6 +473,64 @@ public abstract class AbstractMapMultiSe
     }
 
     //-----------------------------------------------------------------------
+    /**
+     * Returns an array of all of this multiset's elements.
+     *
+     * @return an array of all of this multiset's elements
+     */
+    @Override
+    public Object[] toArray() {
+        final Object[] result = new Object[size()];
+        int i = 0;
+        for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
+            final E current = entry.getKey();
+            final MutableInteger count = entry.getValue();
+            for (int index = count.value; index > 0; index--) {
+                result[i++] = current;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 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 null.
+     *
+     * @param <T> the type of the array elements
+     * @param array the array to populate
+     * @return an array of all of this multiset's elements
+     * @throws ArrayStoreException if the runtime type of the specified array is not
+     *   a supertype of the runtime type of the elements in this list
+     * @throws NullPointerException if the specified array is null
+     */
+    @Override
+    public <T> T[] toArray(T[] array) {
+        final int size = size();
+        if (array.length < size) {
+            @SuppressWarnings("unchecked") // safe as both are of type T
+            final T[] unchecked = (T[]) Array.newInstance(array.getClass().getComponentType(), size);
+            array = unchecked;
+        }
+
+        int i = 0;
+        for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
+            final E current = entry.getKey();
+            final MutableInteger count = entry.getValue();
+            for (int index = count.value; index > 0; index--) {
+                // unsafe, will throw ArrayStoreException if types are not compatible, see javadoc
+                @SuppressWarnings("unchecked")
+                final T unchecked = (T) current;
+                array[i++] = unchecked;
+            }
+        }
+        while (i < array.length) {
+            array[i++] = null;
+        }
+        return array;
+    }
+
+    //-----------------------------------------------------------------------
     @Override
     public boolean equals(final Object object) {
         if (object == this) {
@@ -742,32 +561,4 @@ public abstract class AbstractMapMultiSe
         }
         return total;
     }
-
-    /**
-     * Implement a toString() method suitable for debugging.
-     *
-     * @return a debugging toString
-     */
-    @Override
-    public String toString() {
-        if (size() == 0) {
-            return "[]";
-        }
-        final StringBuilder buf = new StringBuilder();
-        buf.append('[');
-        final Iterator<E> it = uniqueSet().iterator();
-        while (it.hasNext()) {
-            final Object current = it.next();
-            final int count = getCount(current);
-            buf.append(current);
-            buf.append(':');
-            buf.append(count);
-            if (it.hasNext()) {
-                buf.append(", ");
-            }
-        }
-        buf.append(']');
-        return buf.toString();
-    }
-
 }

Added: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java?rev=1715563&view=auto
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java (added)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java Sat Nov 21 20:13:35 2015
@@ -0,0 +1,509 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.collections4.multiset;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.commons.collections4.IteratorUtils;
+import org.apache.commons.collections4.MultiSet;
+import org.apache.commons.collections4.Transformer;
+
+/**
+ * Abstract implementation of the {@link MultiSet} interface to simplify the
+ * creation of subclass implementations.
+ *
+ * @since 4.1
+ * @version $Id$
+ */
+public abstract class AbstractMultiSet<E> extends AbstractCollection<E> implements MultiSet<E> {
+
+    /** View of the elements */
+    private transient Set<E> uniqueSet;
+    /** View of the entries */
+    private transient Set<Entry<E>> entrySet;
+
+    /**
+     * Constructor needed for subclass serialisation.
+     */
+    protected AbstractMultiSet() {
+        super();
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns the number of elements in this multiset.
+     *
+     * @return current size of the multiset
+     */
+    @Override
+    public int size() {
+        int totalSize = 0;
+        for (Entry<E> entry : entrySet()) {
+            totalSize += entry.getCount();
+        }
+        return totalSize;
+    }
+
+    /**
+     * Returns the number of occurrence of the given element in this multiset by
+     * iterating over its entrySet.
+     *
+     * @param object the object to search for
+     * @return the number of occurrences of the object, zero if not found
+     */
+    @Override
+    public int getCount(final Object object) {
+        for (Entry<E> entry : entrySet()) {
+            final E element = entry.getElement();
+            if (element == object ||
+                element != null && element.equals(object)) {
+                return entry.getCount();
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public int setCount(final E object, final int count) {
+        if (count < 0) {
+            throw new IllegalArgumentException("Count must not be negative.");
+        }
+
+        int oldCount = getCount(object);
+        if (oldCount < count) {
+            add(object, count - oldCount);
+        } else {
+            remove(object, oldCount - count);
+        }
+        return oldCount;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Determines if the multiset contains the given element.
+     *
+     * @param object the object to search for
+     * @return true if the multiset contains the given element
+     */
+    @Override
+    public boolean contains(final Object object) {
+        return getCount(object) > 0;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Gets an iterator over the multiset elements. Elements present in the
+     * MultiSet more than once will be returned repeatedly.
+     *
+     * @return the iterator
+     */
+    @Override
+    public Iterator<E> iterator() {
+        return new MultiSetIterator<E>(this);
+    }
+
+    /**
+     * Inner class iterator for the MultiSet.
+     */
+    private static class MultiSetIterator<E> implements Iterator<E> {
+        private final AbstractMultiSet<E> parent;
+        private final Iterator<Entry<E>> entryIterator;
+        private Entry<E> current;
+        private int itemCount;
+        private boolean canRemove;
+
+        /**
+         * Constructor.
+         *
+         * @param parent the parent multiset
+         */
+        public MultiSetIterator(final AbstractMultiSet<E> parent) {
+            this.parent = parent;
+            this.entryIterator = parent.entrySet().iterator();
+            this.current = null;
+            this.canRemove = false;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public boolean hasNext() {
+            return itemCount > 0 || entryIterator.hasNext();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public E next() {
+            if (itemCount == 0) {
+                current = entryIterator.next();
+                itemCount = current.getCount();
+            }
+            canRemove = true;
+            itemCount--;
+            return current.getElement();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void remove() {
+            if (canRemove == false) {
+                throw new IllegalStateException();
+            }
+            final int count = current.getCount();
+            if (count > 1) {
+                parent.remove(current.getElement());
+            } else {
+                entryIterator.remove();
+            }
+            canRemove = false;
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    @Override
+    public boolean add(final E object) {
+        add(object, 1);
+        return true;
+    }
+
+    @Override
+    public int add(final E object, final int occurrences) {
+        throw new UnsupportedOperationException();
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Clears the multiset removing all elements from the entrySet.
+     */
+    @Override
+    public void clear() {
+        Iterator<Entry<E>> it = entrySet().iterator();
+        while (it.hasNext()) {
+            it.next();
+            it.remove();
+        }
+    }
+
+    @Override
+    public boolean remove(final Object object) {
+        return remove(object, 1) != 0;
+    }
+
+    @Override
+    public int remove(final Object object, final int occurrences) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean removeAll(final Collection<?> coll) {
+        boolean result = false;
+        final Iterator<?> i = coll.iterator();
+        while (i.hasNext()) {
+            final Object obj = i.next();
+            final boolean changed = remove(obj, getCount(obj)) != 0;
+            result = result || changed;
+        }
+        return result;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns a view of the unique elements of this multiset.
+     *
+     * @return the set of unique elements in this multiset
+     */
+    @Override
+    public Set<E> uniqueSet() {
+        if (uniqueSet == null) {
+            uniqueSet = createUniqueSet();
+        }
+        return uniqueSet;
+    }
+
+    /**
+     * Create a new view for the set of unique elements in this multiset.
+     *
+     * @return a view of the set of unique elements
+     */
+    protected Set<E> createUniqueSet() {
+        return new UniqueSet<E>(this);
+    }
+
+    /**
+     * Creates a unique set iterator.
+     * Subclasses can override this to return iterators with different properties.
+     *
+     * @return the uniqueSet iterator
+     */
+    protected Iterator<E> createUniqueSetIterator() {
+        final Transformer<Entry<E>, E> transformer = new Transformer<Entry<E>, E>() {
+            @Override
+            public E transform(Entry<E> entry) {
+                return entry.getElement();
+            }
+        };
+        return IteratorUtils.transformedIterator(entrySet().iterator(), transformer);
+    }
+
+    /**
+     * Returns an unmodifiable view of the entries of this multiset.
+     *
+     * @return the set of entries in this multiset
+     */
+    @Override
+    public Set<Entry<E>> entrySet() {
+        if (entrySet == null) {
+            entrySet = createEntrySet();
+        }
+        return entrySet;
+    }
+
+    /**
+     * Create a new view for the set of entries in this multiset.
+     *
+     * @return a view of the set of entries
+     */
+    protected Set<Entry<E>> createEntrySet() {
+        return new EntrySet<E>(this);
+    }
+
+    /**
+     * Returns the number of unique elements in this multiset.
+     *
+     * @return the number of unique elements
+     */
+    protected abstract int uniqueElements();
+
+    /**
+     * Creates an entry set iterator.
+     * Subclasses can override this to return iterators with different properties.
+     *
+     * @return the entrySet iterator
+     */
+    protected abstract Iterator<Entry<E>> createEntrySetIterator();
+
+    //-----------------------------------------------------------------------
+    /**
+     * Inner class UniqueSet.
+     */
+    protected static class UniqueSet<E> extends AbstractSet<E> {
+
+        /** The parent multiset */
+        protected final AbstractMultiSet<E> parent;
+
+        /**
+         * Constructs a new unique element view of the MultiSet.
+         *
+         * @param parent  the parent MultiSet
+         */
+        protected UniqueSet(final AbstractMultiSet<E> parent) {
+            this.parent = parent;
+        }
+
+        @Override
+        public Iterator<E> iterator() {
+            return parent.createUniqueSetIterator();
+        }
+
+        @Override
+        public boolean contains(final Object key) {
+            return parent.contains(key);
+        }
+
+        @Override
+        public boolean containsAll(final Collection<?> coll) {
+            return parent.containsAll(coll);
+        }
+
+        @Override
+        public boolean remove(final Object key) {
+            return parent.remove(key, parent.getCount(key)) != 0;
+        }
+
+        @Override
+        public int size() {
+            return parent.uniqueElements();
+        }
+
+        @Override
+        public void clear() {
+            parent.clear();
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Inner class EntrySet.
+     */
+    protected static class EntrySet<E> extends AbstractSet<Entry<E>> {
+
+        private final AbstractMultiSet<E> parent;
+
+        /**
+         * Constructs a new view of the MultiSet.
+         *
+         * @param parent  the parent MultiSet
+         */
+        protected EntrySet(final AbstractMultiSet<E> parent) {
+            this.parent = parent;
+        }
+
+        @Override
+        public int size() {
+            return parent.uniqueElements();
+        }
+
+        @Override
+        public Iterator<Entry<E>> iterator() {
+            return parent.createEntrySetIterator();
+        }
+
+        @Override
+        public boolean contains(final Object obj) {
+            if (obj instanceof Entry<?> == false) {
+                return false;
+            }
+            final Entry<?> entry = (Entry<?>) obj;
+            final Object element = entry.getElement();
+            return parent.getCount(element) == entry.getCount();
+        }
+
+        @Override
+        public boolean remove(final Object obj) {
+            if (obj instanceof Entry<?> == false) {
+                return false;
+            }
+            final Entry<?> entry = (Entry<?>) obj;
+            final Object element = entry.getElement();
+            if (parent.contains(element)) {
+                final int count = parent.getCount(element);
+                if (entry.getCount() == count) {
+                    parent.remove(element, count);
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Inner class AbstractEntry.
+     */
+    protected static abstract class AbstractEntry<E> implements Entry<E> {
+
+        @Override
+        public boolean equals(Object object) {
+          if (object instanceof Entry) {
+            final Entry<?> other = (Entry<?>) object;
+            final E element = this.getElement();
+            final Object otherElement = other.getElement();
+
+            return this.getCount() == other.getCount() &&
+                   (element == otherElement ||
+                    element != null && element.equals(otherElement));
+          }
+          return false;
+        }
+
+        @Override
+        public int hashCode() {
+          final E element = getElement();
+          return ((element == null) ? 0 : element.hashCode()) ^ getCount();
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s:%d", getElement(), getCount());
+        }
+
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Write the multiset out using a custom routine.
+     * @param out the output stream
+     * @throws IOException any of the usual I/O related exceptions
+     */
+    protected void doWriteObject(final ObjectOutputStream out) throws IOException {
+        out.writeInt(entrySet().size());
+        for (final Entry<E> entry : entrySet()) {
+            out.writeObject(entry.getElement());
+            out.writeInt(entry.getCount());
+        }
+    }
+
+    /**
+     * Read the multiset in using a custom routine.
+     * @param in the input stream
+     * @throws IOException any of the usual I/O related exceptions
+     * @throws ClassNotFoundException if the stream contains an object which class can not be loaded
+     * @throws ClassCastException if the stream does not contain the correct objects
+     */
+    protected void doReadObject(final ObjectInputStream in)
+            throws IOException, ClassNotFoundException {
+        final int entrySize = in.readInt();
+        for (int i = 0; i < entrySize; i++) {
+            @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
+            final E obj = (E) in.readObject();
+            final int count = in.readInt();
+            setCount(obj, count);
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    @Override
+    public boolean equals(final Object object) {
+        if (object == this) {
+            return true;
+        }
+        if (object instanceof MultiSet == false) {
+            return false;
+        }
+        final MultiSet<?> other = (MultiSet<?>) object;
+        if (other.size() != size()) {
+            return false;
+        }
+        for (final Entry<E> entry : entrySet()) {
+            if (other.getCount(entry.getElement()) != getCount(entry.getElement())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return entrySet().hashCode();
+    }
+
+    /**
+     * Implement a toString() method suitable for debugging.
+     *
+     * @return a debugging toString
+     */
+    @Override
+    public String toString() {
+        return entrySet().toString();
+    }
+
+}

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java
------------------------------------------------------------------------------
    svn:keywords = Id Revision HeadURL

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/HashMultiSet.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/HashMultiSet.java?rev=1715563&r1=1715562&r2=1715563&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/HashMultiSet.java (original)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multiset/HashMultiSet.java Sat Nov 21 20:13:35 2015
@@ -70,7 +70,8 @@ public class HashMultiSet<E> extends Abs
      */
     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
-        super.doReadObject(new HashMap<E, MutableInteger>(), in);
+        setMap(new HashMap<E, MutableInteger>());
+        super.doReadObject(in);
     }
 
 }