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:
parent
52372e8f2b
commit
ffe13cfcca
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue