From ffe13cfcca913a3eff9cec27f0a3ad2b6dcedcc8 Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Sat, 21 Nov 2015 20:13:35 +0000 Subject: [PATCH] Add abstract class to simplify creation of MultiSet implementations, needed for MultiValuedMap; additional cleanup. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1715563 13f79535-47bb-0310-9956-ffa450edef68 --- .../multiset/AbstractMapMultiSet.java | 381 +++---------- .../multiset/AbstractMultiSet.java | 509 ++++++++++++++++++ .../collections4/multiset/HashMultiSet.java | 3 +- 3 files changed, 597 insertions(+), 296 deletions(-) create mode 100644 src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java diff --git a/src/main/java/org/apache/commons/collections4/multiset/AbstractMapMultiSet.java b/src/main/java/org/apache/commons/collections4/multiset/AbstractMapMultiSet.java index c6ca6b201..0336f28df 100644 --- a/src/main/java/org/apache/commons/collections4/multiset/AbstractMapMultiSet.java +++ b/src/main/java/org/apache/commons/collections4/multiset/AbstractMapMultiSet.java @@ -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.iterators.AbstractIteratorDecorator; * @since 4.1 * @version $Id$ */ -public abstract class AbstractMapMultiSet extends AbstractCollection implements MultiSet { +public abstract class AbstractMapMultiSet extends AbstractMultiSet { /** The map to use to store the data */ private transient Map map; @@ -51,10 +46,6 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple private transient int size; /** The modification count for fail fast iterators */ private transient int modCount; - /** View of the elements */ - private transient Set uniqueSet; - /** View of the entries */ - private transient Set> entrySet; /** * Constructor needed for subclass serialisation. @@ -78,12 +69,23 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple * 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 getMap() { return map; } + /** + * Sets the map being wrapped. + *

+ * NOTE: this method should only be used during deserialization + * + * @param map the map to wrap + */ + protected void setMap(Map map) { + this.map = map; + } + //----------------------------------------------------------------------- /** * Returns the number of elements in this multiset. @@ -121,21 +123,6 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple 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 AbstractMapMultiSet extends AbstractCollection imple */ @Override public Iterator iterator() { - return new MultiSetIterator(this); + return new MapBasedMultiSetIterator(this); } /** * Inner class iterator for the MultiSet. */ - static class MultiSetIterator implements Iterator { + private static class MapBasedMultiSetIterator implements Iterator { private final AbstractMapMultiSet parent; private final Iterator> entryIterator; private Map.Entry current; @@ -177,7 +164,7 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple * * @param parent the parent multiset */ - public MultiSetIterator(final AbstractMapMultiSet parent) { + public MapBasedMultiSetIterator(final AbstractMapMultiSet parent) { this.parent = parent; this.entryIterator = parent.map.entrySet().iterator(); this.current = null; @@ -227,12 +214,6 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple } //----------------------------------------------------------------------- - @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) { @@ -265,11 +246,6 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple size = 0; } - @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) { @@ -294,18 +270,6 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple 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,164 +301,22 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple } //----------------------------------------------------------------------- - /** - * 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 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; + protected Iterator createUniqueSetIterator() { + return new UniqueSetIterator(getMap().keySet().iterator(), this); } - /** - * 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 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[] 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 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 int uniqueElements() { + return map.size(); } - /** - * Returns a view of the underlying map's key set. - * - * @return the set of unique elements in this multiset - */ @Override - public Set uniqueSet() { - if (uniqueSet == null) { - uniqueSet = new UniqueSet(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 createUniqueSetIterator(final Iterator iterator) { - return new UniqueSetIterator(iterator, this); - } - - /** - * Returns an unmodifiable view of the underlying map's key set. - * - * @return the set of unique elements in this multiset - */ - @Override - public Set> entrySet() { - if (entrySet == null) { - entrySet = new EntrySet(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> createEntrySetIterator(final Iterator> iterator) { - return new EntrySetIterator(iterator, this); + protected Iterator> createEntrySetIterator() { + return new EntrySetIterator(map.entrySet().iterator(), this); } //----------------------------------------------------------------------- - /** - * Inner class UniqueSet. - */ - protected static class UniqueSet extends AbstractCollectionDecorator implements Set { - - /** Serialization version */ - private static final long serialVersionUID = 20150610L; - - /** The parent multiset */ - protected final AbstractMapMultiSet parent; - - /** - * Constructs a new unique element view of the MultiSet. - * - * @param parent the parent MultiSet - */ - protected UniqueSet(final AbstractMapMultiSet parent) { - super(parent.map.keySet()); - this.parent = parent; - } - - @Override - public Iterator 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. */ @@ -539,61 +361,6 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple } } - //----------------------------------------------------------------------- - /** - * Inner class EntrySet. - */ - protected static class EntrySet extends AbstractSet> { - - private final AbstractMapMultiSet parent; - - /** - * Constructs a new view of the MultiSet. - * - * @param parent the parent MultiSet - */ - protected EntrySet(final AbstractMapMultiSet parent) { - this.parent = parent; - } - - @Override - public int size() { - return parent.map.entrySet().size(); - } - - @Override - public Iterator> 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 AbstractMapMultiSet extends AbstractCollection imple /** * Inner class MultiSetEntry. */ - protected static class MultiSetEntry implements Entry { + protected static class MultiSetEntry extends AbstractEntry { protected final Map.Entry parentEntry; @@ -668,20 +435,15 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple 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 entry : map.entrySet()) { @@ -691,16 +453,15 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple } /** - * 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 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 @@ -711,6 +472,64 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple } } + //----------------------------------------------------------------------- + /** + * 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 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 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[] 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 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) { @@ -742,32 +561,4 @@ public abstract class AbstractMapMultiSet extends AbstractCollection imple } 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 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(); - } - } diff --git a/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java b/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java new file mode 100644 index 000000000..5e26869ae --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java @@ -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 extends AbstractCollection implements MultiSet { + + /** View of the elements */ + private transient Set uniqueSet; + /** View of the entries */ + private transient Set> 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 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 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 iterator() { + return new MultiSetIterator(this); + } + + /** + * Inner class iterator for the MultiSet. + */ + private static class MultiSetIterator implements Iterator { + private final AbstractMultiSet parent; + private final Iterator> entryIterator; + private Entry current; + private int itemCount; + private boolean canRemove; + + /** + * Constructor. + * + * @param parent the parent multiset + */ + public MultiSetIterator(final AbstractMultiSet 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> 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 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 createUniqueSet() { + return new UniqueSet(this); + } + + /** + * Creates a unique set iterator. + * Subclasses can override this to return iterators with different properties. + * + * @return the uniqueSet iterator + */ + protected Iterator createUniqueSetIterator() { + final Transformer, E> transformer = new Transformer, E>() { + @Override + public E transform(Entry 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> 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> createEntrySet() { + return new EntrySet(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> createEntrySetIterator(); + + //----------------------------------------------------------------------- + /** + * Inner class UniqueSet. + */ + protected static class UniqueSet extends AbstractSet { + + /** The parent multiset */ + protected final AbstractMultiSet parent; + + /** + * Constructs a new unique element view of the MultiSet. + * + * @param parent the parent MultiSet + */ + protected UniqueSet(final AbstractMultiSet parent) { + this.parent = parent; + } + + @Override + public Iterator 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 extends AbstractSet> { + + private final AbstractMultiSet parent; + + /** + * Constructs a new view of the MultiSet. + * + * @param parent the parent MultiSet + */ + protected EntrySet(final AbstractMultiSet parent) { + this.parent = parent; + } + + @Override + public int size() { + return parent.uniqueElements(); + } + + @Override + public Iterator> 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 implements Entry { + + @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 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 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(); + } + +} diff --git a/src/main/java/org/apache/commons/collections4/multiset/HashMultiSet.java b/src/main/java/org/apache/commons/collections4/multiset/HashMultiSet.java index a30499925..5648df947 100644 --- a/src/main/java/org/apache/commons/collections4/multiset/HashMultiSet.java +++ b/src/main/java/org/apache/commons/collections4/multiset/HashMultiSet.java @@ -70,7 +70,8 @@ public class HashMultiSet extends AbstractMapMultiSet implements Serializa */ private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); - super.doReadObject(new HashMap(), in); + setMap(new HashMap()); + super.doReadObject(in); } }