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
This commit is contained in:
Thomas Neidhart 2015-11-21 20:13:35 +00:00
parent 52372e8f2b
commit ffe13cfcca
3 changed files with 597 additions and 296 deletions

View File

@ -20,16 +20,11 @@ import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.ConcurrentModificationException; import java.util.ConcurrentModificationException;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.apache.commons.collections4.MultiSet; import org.apache.commons.collections4.MultiSet;
import org.apache.commons.collections4.collection.AbstractCollectionDecorator;
import org.apache.commons.collections4.iterators.AbstractIteratorDecorator; import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
/** /**
@ -43,7 +38,7 @@ import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
* @since 4.1 * @since 4.1
* @version $Id$ * @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 */ /** The map to use to store the data */
private transient Map<E, MutableInteger> map; private transient Map<E, MutableInteger> map;
@ -51,10 +46,6 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
private transient int size; private transient int size;
/** The modification count for fail fast iterators */ /** The modification count for fail fast iterators */
private transient int modCount; 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. * Constructor needed for subclass serialisation.
@ -78,12 +69,23 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
* Utility method for implementations to access the map that backs this multiset. * Utility method for implementations to access the map that backs this multiset.
* Not intended for interactive use outside of subclasses. * 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() { protected Map<E, MutableInteger> getMap() {
return map; 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. * Returns the number of elements in this multiset.
@ -121,21 +123,6 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
return 0; 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 * Determines if the multiset contains the given element by checking if the
@ -158,13 +145,13 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
*/ */
@Override @Override
public Iterator<E> iterator() { public Iterator<E> iterator() {
return new MultiSetIterator<E>(this); return new MapBasedMultiSetIterator<E>(this);
} }
/** /**
* Inner class iterator for the MultiSet. * 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 AbstractMapMultiSet<E> parent;
private final Iterator<Map.Entry<E, MutableInteger>> entryIterator; private final Iterator<Map.Entry<E, MutableInteger>> entryIterator;
private Map.Entry<E, MutableInteger> current; private Map.Entry<E, MutableInteger> current;
@ -177,7 +164,7 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
* *
* @param parent the parent multiset * @param parent the parent multiset
*/ */
public MultiSetIterator(final AbstractMapMultiSet<E> parent) { public MapBasedMultiSetIterator(final AbstractMapMultiSet<E> parent) {
this.parent = parent; this.parent = parent;
this.entryIterator = parent.map.entrySet().iterator(); this.entryIterator = parent.map.entrySet().iterator();
this.current = null; this.current = null;
@ -227,12 +214,6 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@Override
public boolean add(final E object) {
add(object, 1);
return true;
}
@Override @Override
public int add(final E object, final int occurrences) { public int add(final E object, final int occurrences) {
if (occurrences < 0) { if (occurrences < 0) {
@ -265,11 +246,6 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
size = 0; size = 0;
} }
@Override
public boolean remove(final Object object) {
return remove(object, 1) != 0;
}
@Override @Override
public int remove(final Object object, final int occurrences) { public int remove(final Object object, final int occurrences) {
if (occurrences < 0) { if (occurrences < 0) {
@ -294,18 +270,6 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
return oldCount; 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. * Mutable integer class for storing the data.
@ -337,164 +301,22 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/**
* Returns an array of all of this multiset's elements.
*
* @return an array of all of this multiset's elements
*/
@Override @Override
public Object[] toArray() { protected Iterator<E> createUniqueSetIterator() {
final Object[] result = new Object[size()]; return new UniqueSetIterator<E>(getMap().keySet().iterator(), this);
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 @Override
public <T> T[] toArray(T[] array) { protected int uniqueElements() {
final int size = size(); return map.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;
}
/**
* Returns a view of the underlying map's key set.
*
* @return the set of unique elements in this multiset
*/
@Override @Override
public Set<E> uniqueSet() { protected Iterator<Entry<E>> createEntrySetIterator() {
if (uniqueSet == null) { return new EntrySetIterator<E>(map.entrySet().iterator(), this);
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);
}
/**
* 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);
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/**
* 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. * Inner class UniqueSetIterator.
*/ */
@ -539,61 +361,6 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
} }
} }
//-----------------------------------------------------------------------
/**
* 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. * Inner class EntrySetIterator.
*/ */
@ -647,7 +414,7 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
/** /**
* Inner class MultiSetEntry. * 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; protected final Map.Entry<E, MutableInteger> parentEntry;
@ -668,20 +435,15 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
public int getCount() { public int getCount() {
return parentEntry.getValue().value; 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 * @param out the output stream
* @throws IOException any of the usual I/O related exceptions * @throws IOException any of the usual I/O related exceptions
*/ */
@Override
protected void doWriteObject(final ObjectOutputStream out) throws IOException { protected void doWriteObject(final ObjectOutputStream out) throws IOException {
out.writeInt(map.size()); out.writeInt(map.size());
for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) { for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
@ -691,16 +453,15 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
} }
/** /**
* Read the map in using a custom routine. * Read the multiset in using a custom routine.
* @param map the map to use
* @param in the input stream * @param in the input stream
* @throws IOException any of the usual I/O related exceptions * @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 ClassNotFoundException if the stream contains an object which class can not be loaded
* @throws ClassCastException if the stream does not contain the correct objects * @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 { throws IOException, ClassNotFoundException {
this.map = map;
final int entrySize = in.readInt(); final int entrySize = in.readInt();
for (int i = 0; i < entrySize; i++) { for (int i = 0; i < entrySize; i++) {
@SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
@ -711,6 +472,64 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> 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<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 @Override
public boolean equals(final Object object) { public boolean equals(final Object object) {
@ -742,32 +561,4 @@ public abstract class AbstractMapMultiSet<E> extends AbstractCollection<E> imple
} }
return total; 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();
}
} }

View File

@ -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();
}
}

View File

@ -70,7 +70,8 @@ public class HashMultiSet<E> extends AbstractMapMultiSet<E> implements Serializa
*/ */
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject(); in.defaultReadObject();
super.doReadObject(new HashMap<E, MutableInteger>(), in); setMap(new HashMap<E, MutableInteger>());
super.doReadObject(in);
} }
} }