From b2b8f4adc557e4ef1ee2fe5e0ab46866c06ec55b Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Thu, 19 Nov 2015 23:08:01 +0000 Subject: [PATCH] [COLLECTIONS-580] Do not use InstantiateFactory anymore for MultiValuedMaps: different MultiValuedMap implementations are now fully typed for the used underlying map and value collection class being used. This has pros and cons, but it is certainly safer to do it that way. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1715302 13f79535-47bb-0310-9956-ffa450edef68 --- .../commons/collections4/MultiMapUtils.java | 37 +-- .../multimap/AbstractListValuedMap.java | 62 ++--- .../multimap/AbstractMultiValuedMap.java | 127 +++++---- .../AbstractMultiValuedMapDecorator.java | 2 + .../multimap/AbstractSetValuedMap.java | 57 ++-- .../multimap/ArrayListValuedHashMap.java | 128 +++++++++ .../multimap/HashSetValuedHashMap.java | 116 ++++++++ .../multimap/MultiValuedHashMap.java | 259 ------------------ .../multimap/TransformedMultiValuedMap.java | 2 +- .../collections4/multimap/package-info.java | 5 +- .../collections4/MultiMapUtilsTest.java | 16 +- .../multimap/AbstractMultiValuedMapTest.java | 24 +- ...t.java => ArrayListValuedHashMapTest.java} | 117 ++------ .../multimap/HashSetValuedHashMapTest.java | 136 +++++++++ .../TransformedMultiValuedMapTest.java | 24 +- .../UnmodifiableMultiValuedMapTest.java | 19 +- ...luedHashMap.emptyCollection.version4.1.obj | Bin 0 -> 117 bytes ...aluedHashMap.fullCollection.version4.1.obj | Bin 0 -> 194 bytes ...luedHashMap.emptyCollection.version4.1.obj | Bin 0 -> 114 bytes ...aluedHashMap.fullCollection.version4.1.obj | Bin 0 -> 191 bytes ...tiValuedMap.emptyCollection.version4.1.obj | Bin 945 -> 510 bytes ...ltiValuedMap.fullCollection.version4.1.obj | Bin 1055 -> 587 bytes ...tiValuedMap.emptyCollection.version4.1.obj | Bin 780 -> 345 bytes ...ltiValuedMap.fullCollection.version4.1.obj | Bin 890 -> 422 bytes 24 files changed, 574 insertions(+), 557 deletions(-) create mode 100644 src/main/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMap.java create mode 100644 src/main/java/org/apache/commons/collections4/multimap/HashSetValuedHashMap.java delete mode 100644 src/main/java/org/apache/commons/collections4/multimap/MultiValuedHashMap.java rename src/test/java/org/apache/commons/collections4/multimap/{MultiValuedHashMapTest.java => ArrayListValuedHashMapTest.java} (52%) create mode 100644 src/test/java/org/apache/commons/collections4/multimap/HashSetValuedHashMapTest.java create mode 100644 src/test/resources/data/test/ArrayListValuedHashMap.emptyCollection.version4.1.obj create mode 100644 src/test/resources/data/test/ArrayListValuedHashMap.fullCollection.version4.1.obj create mode 100644 src/test/resources/data/test/HashSetValuedHashMap.emptyCollection.version4.1.obj create mode 100644 src/test/resources/data/test/HashSetValuedHashMap.fullCollection.version4.1.obj diff --git a/src/main/java/org/apache/commons/collections4/MultiMapUtils.java b/src/main/java/org/apache/commons/collections4/MultiMapUtils.java index bf30d9006..5fdee52c1 100644 --- a/src/main/java/org/apache/commons/collections4/MultiMapUtils.java +++ b/src/main/java/org/apache/commons/collections4/MultiMapUtils.java @@ -23,7 +23,8 @@ import java.util.List; import java.util.Set; import org.apache.commons.collections4.bag.HashBag; -import org.apache.commons.collections4.multimap.MultiValuedHashMap; +import org.apache.commons.collections4.multimap.ArrayListValuedHashMap; +import org.apache.commons.collections4.multimap.HashSetValuedHashMap; import org.apache.commons.collections4.multimap.TransformedMultiValuedMap; import org.apache.commons.collections4.multimap.UnmodifiableMultiValuedMap; @@ -52,7 +53,7 @@ public class MultiMapUtils { */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static final MultiValuedMap EMPTY_MULTI_VALUED_MAP = - UnmodifiableMultiValuedMap.unmodifiableMultiValuedMap(new MultiValuedHashMap()); + UnmodifiableMultiValuedMap.unmodifiableMultiValuedMap(new ArrayListValuedHashMap(0, 0)); /** * Returns immutable EMPTY_MULTI_VALUED_MAP with generic type safety. @@ -188,21 +189,7 @@ public class MultiMapUtils { * @return a new ListValuedMap */ public static ListValuedMap newListValuedHashMap() { - return MultiValuedHashMap.listValuedHashMap(); - } - - /** - * Creates a {@link ListValuedMap} with a {@link java.util.HashMap HashMap} as its internal - * storage which maps the keys to list of type listClass. - * - * @param the key type - * @param the value type - * @param the List class type - * @param listClass the class of the list - * @return a new {@link ListValuedMap} - */ - public static > ListValuedMap newListValuedHashMap(final Class listClass) { - return MultiValuedHashMap.listValuedHashMap(listClass); + return new ArrayListValuedHashMap(); } /** @@ -214,21 +201,7 @@ public class MultiMapUtils { * @return a new {@link SetValuedMap} */ public static SetValuedMap newSetValuedHashMap() { - return MultiValuedHashMap.setValuedHashMap(); - } - - /** - * Creates a {@link SetValuedMap} with a {@link java.util.HashMap HashMap} as its internal - * storage which maps the keys to a set of type setClass - * - * @param the key type - * @param the value type - * @param the Set class type - * @param setClass the class of the set - * @return a new {@link SetValuedMap} - */ - public static > SetValuedMap newSetValuedHashMap(final Class setClass) { - return MultiValuedHashMap.setValuedHashMap(setClass); + return new HashSetValuedHashMap(); } // MultiValuedMap Decorators diff --git a/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java b/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java index dd1014eb4..d5e3237a3 100644 --- a/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java +++ b/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java @@ -38,34 +38,28 @@ import org.apache.commons.collections4.ListValuedMap; public abstract class AbstractListValuedMap extends AbstractMultiValuedMap implements ListValuedMap { - /** The serialization version */ - private static final long serialVersionUID = 20150612L; - /** - * A constructor that wraps, not copies - * - * @param the list type - * @param map the map to wrap, must not be null - * @param listClazz the collection class - * @throws NullPointerException if the map is null + * Constructor needed for subclass serialisation. */ - protected > AbstractListValuedMap(final Map map, Class listClazz) { - super(map, listClazz); + protected AbstractListValuedMap() { + super(); } /** * A constructor that wraps, not copies * - * @param the list type * @param map the map to wrap, must not be null - * @param listClazz the collection class - * @param initialListCapacity the initial size of the values list - * @throws NullPointerException if the map is null - * @throws IllegalArgumentException if initialListCapacity is negative + * @throws NullPointerException if the map is null */ - protected > AbstractListValuedMap(final Map map, Class listClazz, - final int initialListCapacity) { - super(map, listClazz, initialListCapacity); + protected AbstractListValuedMap(final Map> map) { + super(map); + } + + // ----------------------------------------------------------------------- + @Override + @SuppressWarnings("unchecked") + protected Map> getMap() { + return (Map>) super.getMap(); } /** @@ -73,10 +67,9 @@ public abstract class AbstractListValuedMap extends AbstractMultiValuedMap * @return a new list */ @Override - protected List createCollection() { - return (List) super.createCollection(); - } + protected abstract List createCollection(); + // ----------------------------------------------------------------------- /** * Gets the list of values associated with the specified key. This would * return an empty list in case the mapping is not present @@ -100,25 +93,10 @@ public abstract class AbstractListValuedMap extends AbstractMultiValuedMap */ @Override public List remove(Object key) { - return ListUtils.emptyIfNull((List) getMap().remove(key)); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof ListValuedMap) { - return asMap().equals(((ListValuedMap) obj).asMap()); - } - return false; - } - - @Override - public int hashCode() { - return asMap().hashCode(); + return ListUtils.emptyIfNull(getMap().remove(key)); } + // ----------------------------------------------------------------------- /** * Wrapped list to handle add and remove on the list returned by get(object) */ @@ -130,7 +108,7 @@ public abstract class AbstractListValuedMap extends AbstractMultiValuedMap @Override protected List getMapping() { - return (List) getMap().get(key); + return getMap().get(key); } @Override @@ -237,13 +215,13 @@ public abstract class AbstractListValuedMap extends AbstractMultiValuedMap public ValuesListIterator(final K key) { this.key = key; - this.values = ListUtils.emptyIfNull((List) getMap().get(key)); + this.values = ListUtils.emptyIfNull(getMap().get(key)); this.iterator = values.listIterator(); } public ValuesListIterator(final K key, int index) { this.key = key; - this.values = ListUtils.emptyIfNull((List) getMap().get(key)); + this.values = ListUtils.emptyIfNull(getMap().get(key)); this.iterator = values.listIterator(index); } diff --git a/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java b/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java index fe0a77076..f1a1e5a01 100644 --- a/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java +++ b/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java @@ -16,7 +16,9 @@ */ package org.apache.commons.collections4.multimap; -import java.io.Serializable; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.lang.reflect.Array; import java.util.AbstractCollection; import java.util.ArrayList; @@ -27,13 +29,11 @@ import java.util.Map.Entry; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.collections4.Factory; import org.apache.commons.collections4.IteratorUtils; import org.apache.commons.collections4.MapIterator; import org.apache.commons.collections4.MultiSet; import org.apache.commons.collections4.MultiValuedMap; import org.apache.commons.collections4.Transformer; -import org.apache.commons.collections4.functors.InstantiateFactory; import org.apache.commons.collections4.iterators.EmptyMapIterator; import org.apache.commons.collections4.iterators.IteratorChain; import org.apache.commons.collections4.iterators.LazyIteratorChain; @@ -50,13 +50,7 @@ import org.apache.commons.collections4.set.UnmodifiableSet; * @since 4.1 * @version $Id$ */ -public abstract class AbstractMultiValuedMap implements MultiValuedMap, Serializable { - - /** Serialization Version */ - private static final long serialVersionUID = 20150612L; - - /** The factory for creating value collections. */ - private final Factory> collectionFactory; +public abstract class AbstractMultiValuedMap implements MultiValuedMap { /** The values view */ private transient Collection valuesView; @@ -68,60 +62,55 @@ public abstract class AbstractMultiValuedMap implements MultiValuedMap> map; + private transient Map> map; + + /** + * Constructor needed for subclass serialisation. + */ + protected AbstractMultiValuedMap() { + super(); + } /** * Constructor that wraps (not copies). * * @param the collection type * @param map the map to wrap, must not be null - * @param collectionClazz the collection class * @throws NullPointerException if the map is null */ @SuppressWarnings("unchecked") - protected > AbstractMultiValuedMap(final Map map, - final Class collectionClazz) { + protected AbstractMultiValuedMap(final Map> map) { if (map == null) { throw new NullPointerException("Map must not be null."); } this.map = (Map>) map; - this.collectionFactory = new InstantiateFactory(collectionClazz); - } - - /** - * Constructor that wraps (not copies). - * - * @param the collection type - * @param map the map to wrap, must not be null - * @param collectionClazz the collection class - * @param initialCollectionCapacity the initial capacity of the collection - * @throws NullPointerException if the map is null - * @throws IllegalArgumentException if initialCollectionCapacity is negative - */ - @SuppressWarnings("unchecked") - protected > AbstractMultiValuedMap(final Map map, - final Class collectionClazz, final int initialCollectionCapacity) { - if (map == null) { - throw new NullPointerException("Map must not be null."); - } - if (initialCollectionCapacity < 0) { - throw new IllegalArgumentException("InitialCapacity must not be negative."); - } - this.map = (Map>) map; - this.collectionFactory = new InstantiateFactory(collectionClazz, - new Class[] { Integer.TYPE }, - new Object[] { Integer.valueOf(initialCollectionCapacity) }); } + // ----------------------------------------------------------------------- /** * Gets the map being wrapped. * * @return the wrapped map */ - protected Map> getMap() { + 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 + */ + @SuppressWarnings("unchecked") + protected void setMap(Map> map) { + this.map = (Map>) map; + } + + protected abstract Collection createCollection(); + + // ----------------------------------------------------------------------- @Override public boolean containsKey(Object key) { return getMap().containsKey(key); @@ -250,7 +239,7 @@ public abstract class AbstractMultiValuedMap implements MultiValuedMap implements MultiValuedMap> asMap() { - return getMap(); + // TODO: return a view of the map + return (Map>) getMap(); } /** @@ -373,18 +364,12 @@ public abstract class AbstractMultiValuedMap implements MultiValuedMap createCollection() { - return collectionFactory.create(); + return asMap().toString(); } // ----------------------------------------------------------------------- @@ -906,4 +891,44 @@ public abstract class AbstractMultiValuedMap implements MultiValuedMap> entry : map.entrySet()) { + out.writeObject(entry.getKey()); + out.writeInt(entry.getValue().size()); + for (final V value : entry.getValue()) { + out.writeObject(value); + } + } + } + + /** + * Read the map 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 K key = (K) in.readObject(); + final Collection values = get(key); + final int valueSize = in.readInt(); + for (int j = 0; j < valueSize; j++) { + @SuppressWarnings("unchecked") // see above + V value = (V) in.readObject(); + values.add(value); + } + } + } + } diff --git a/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapDecorator.java b/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapDecorator.java index 525cfc58c..ff520caa4 100644 --- a/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapDecorator.java +++ b/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapDecorator.java @@ -61,6 +61,7 @@ public abstract class AbstractMultiValuedMapDecorator this.map = map; } + // ----------------------------------------------------------------------- /** * The decorated multi-valued map. * @@ -70,6 +71,7 @@ public abstract class AbstractMultiValuedMapDecorator return map; } + // ----------------------------------------------------------------------- @Override public int size() { return decorated().size(); diff --git a/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java b/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java index 5355f6d66..bcfa20af3 100644 --- a/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java +++ b/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java @@ -36,36 +36,38 @@ import org.apache.commons.collections4.SetValuedMap; public abstract class AbstractSetValuedMap extends AbstractMultiValuedMap implements SetValuedMap { - /** Serialization version */ - private static final long serialVersionUID = 20150612L; - /** - * A constructor that wraps, not copies - * - * @param the set type - * @param map the map to wrap, must not be null - * @param setClazz the collection class - * @throws NullPointerException if the map is null + * Constructor needed for subclass serialisation. */ - protected > AbstractSetValuedMap(Map map, Class setClazz) { - super(map, setClazz); + protected AbstractSetValuedMap() { + super(); } /** * A constructor that wraps, not copies * - * @param the set type * @param map the map to wrap, must not be null - * @param setClazz the collection class - * @param initialSetCapacity the initial size of the values set * @throws NullPointerException if the map is null - * @throws IllegalArgumentException if initialSetCapacity is negative */ - protected > AbstractSetValuedMap(Map map, Class setClazz, - int initialSetCapacity) { - super(map, setClazz, initialSetCapacity); + protected AbstractSetValuedMap(Map> map) { + super(map); } + // ----------------------------------------------------------------------- + @Override + @SuppressWarnings("unchecked") + protected Map> getMap() { + return (Map>) super.getMap(); + } + + /** + * Creates a new value collection using the provided factory. + * @return a new list + */ + @Override + protected abstract Set createCollection(); + + // ----------------------------------------------------------------------- /** * Gets the set of values associated with the specified key. This would * return an empty set in case the mapping is not present @@ -90,25 +92,10 @@ public abstract class AbstractSetValuedMap extends AbstractMultiValuedMap< */ @Override public Set remove(Object key) { - return SetUtils.emptyIfNull((Set) getMap().remove(key)); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof SetValuedMap) { - return asMap().equals(((SetValuedMap) obj).asMap()); - } - return false; - } - - @Override - public int hashCode() { - return asMap().hashCode(); + return SetUtils.emptyIfNull(getMap().remove(key)); } + // ----------------------------------------------------------------------- /** * Wrapped set to handle add and remove on the collection returned by * {@code get(Object)}. diff --git a/src/main/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMap.java b/src/main/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMap.java new file mode 100644 index 000000000..2d4fa648a --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMap.java @@ -0,0 +1,128 @@ +package org.apache.commons.collections4.multimap; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.collections4.ListValuedMap; +import org.apache.commons.collections4.MultiValuedMap; + +/** + * Implements a {@link ListValuedMap}, using a {@link HashMap} to provide data + * storage and {@link ArrayList}s as value collections. This is the standard + * implementation of a ListValuedMap. + *

+ * Note that ArrayListValuedHashMap is not synchronized and is not + * thread-safe. If you wish to use this map from multiple threads + * concurrently, you must use appropriate synchronization. This class may throw + * exceptions when accessed by concurrent threads without synchronization. + * + * @since 4.1 + * @version $Id$ + */ +public class ArrayListValuedHashMap extends AbstractListValuedMap + implements Serializable { + + /** Serialization Version */ + private static final long serialVersionUID = 20151118L; + + /** + * The initial map capacity used when none specified in constructor. + */ + private static final int DEFAULT_INITIAL_MAP_CAPACITY = 16; + + /** + * The initial list capacity when using none specified in constructor. + */ + private static final int DEFAULT_INITIAL_LIST_CAPACITY = 3; + + /** + * The initial list capacity when creating a new value collection. + */ + private final int initialListCapacity; + + /** + * Creates an empty ArrayListValuedHashMap with the default initial + * map capacity (16) and the default initial list capacity (3). + */ + public ArrayListValuedHashMap() { + this(DEFAULT_INITIAL_MAP_CAPACITY, DEFAULT_INITIAL_LIST_CAPACITY); + } + + /** + * Creates an empty ArrayListValuedHashMap with the default initial + * map capacity (16) and the specified initial list capacity. + * + * @param initialListCapacity the initial capacity used for value collections + */ + public ArrayListValuedHashMap(int initialListCapacity) { + this(DEFAULT_INITIAL_MAP_CAPACITY, initialListCapacity); + } + + /** + * Creates an empty ArrayListValuedHashMap with the specified initial + * map and list capacities. + * + * @param initialMapCapacity the initial hashmap capacity + * @param initialListCapacity the initial capacity used for value collections + */ + public ArrayListValuedHashMap(int initialMapCapacity, int initialListCapacity) { + super(new HashMap>(initialMapCapacity)); + this.initialListCapacity = initialListCapacity; + } + + /** + * Creates an ArrayListValuedHashMap copying all the mappings of the given map. + * + * @param map a MultiValuedMap to copy into this map + */ + public ArrayListValuedHashMap(final MultiValuedMap map) { + this(map.size(), DEFAULT_INITIAL_LIST_CAPACITY); + super.putAll(map); + } + + /** + * Creates an ArrayListValuedHashMap copying all the mappings of the given map. + * + * @param map a Map to copy into this map + */ + public ArrayListValuedHashMap(final Map map) { + this(map.size(), DEFAULT_INITIAL_LIST_CAPACITY); + super.putAll(map); + } + + // ----------------------------------------------------------------------- + @Override + protected ArrayList createCollection() { + return new ArrayList(initialListCapacity); + } + + // ----------------------------------------------------------------------- + /** + * Trims the capacity of all value collections to their current size. + */ + public void trimToSize() { + for (Collection coll : getMap().values()) { + final ArrayList list = (ArrayList) coll; + list.trimToSize(); + } + } + + // ----------------------------------------------------------------------- + private void writeObject(ObjectOutputStream oos) throws IOException { + oos.defaultWriteObject(); + doWriteObject(oos); + } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + setMap(new HashMap>()); + doReadObject(ois); + } + +} diff --git a/src/main/java/org/apache/commons/collections4/multimap/HashSetValuedHashMap.java b/src/main/java/org/apache/commons/collections4/multimap/HashSetValuedHashMap.java new file mode 100644 index 000000000..5737368c8 --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/multimap/HashSetValuedHashMap.java @@ -0,0 +1,116 @@ +package org.apache.commons.collections4.multimap; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import org.apache.commons.collections4.MultiValuedMap; +import org.apache.commons.collections4.SetValuedMap; + +/** + * Implements a {@link SetValuedMap}, using a {@link HashMap} to provide data + * storage and {@link HashSet}s as value collections. This is the standard + * implementation of a SetValuedMap. + *

+ * Note that HashSetValuedHashMap is not synchronized and is not + * thread-safe. If you wish to use this map from multiple threads + * concurrently, you must use appropriate synchronization. This class may throw + * exceptions when accessed by concurrent threads without synchronization. + * + * @since 4.1 + * @version $Id$ + */ +public class HashSetValuedHashMap extends AbstractSetValuedMap + implements Serializable { + + /** Serialization Version */ + private static final long serialVersionUID = 20151118L; + + /** + * The initial map capacity used when none specified in constructor. + */ + private static final int DEFAULT_INITIAL_MAP_CAPACITY = 16; + + /** + * The initial set capacity when using none specified in constructor. + */ + private static final int DEFAULT_INITIAL_SET_CAPACITY = 3; + + /** + * The initial list capacity when creating a new value collection. + */ + private final int initialSetCapacity; + + /** + * Creates an empty HashSetValuedHashMap with the default initial + * map capacity (16) and the default initial set capacity (3). + */ + public HashSetValuedHashMap() { + this(DEFAULT_INITIAL_MAP_CAPACITY, DEFAULT_INITIAL_SET_CAPACITY); + } + + /** + * Creates an empty HashSetValuedHashMap with the default initial + * map capacity (16) and the specified initial set capacity. + * + * @param initialSetCapacity the initial capacity used for value collections + */ + public HashSetValuedHashMap(int initialSetCapacity) { + this(DEFAULT_INITIAL_MAP_CAPACITY, initialSetCapacity); + } + + /** + * Creates an empty HashSetValuedHashMap with the specified initial + * map and list capacities. + * + * @param initialMapCapacity the initial hashmap capacity + * @param initialSetCapacity the initial capacity used for value collections + */ + public HashSetValuedHashMap(int initialMapCapacity, int initialSetCapacity) { + super(new HashMap>(initialMapCapacity)); + this.initialSetCapacity = initialSetCapacity; + } + + /** + * Creates an HashSetValuedHashMap copying all the mappings of the given map. + * + * @param map a MultiValuedMap to copy into this map + */ + public HashSetValuedHashMap(final MultiValuedMap map) { + this(map.size(), DEFAULT_INITIAL_SET_CAPACITY); + super.putAll(map); + } + + /** + * Creates an HashSetValuedHashMap copying all the mappings of the given map. + * + * @param map a Map to copy into this map + */ + public HashSetValuedHashMap(final Map map) { + this(map.size(), DEFAULT_INITIAL_SET_CAPACITY); + super.putAll(map); + } + + // ----------------------------------------------------------------------- + @Override + protected HashSet createCollection() { + return new HashSet(initialSetCapacity); + } + + // ----------------------------------------------------------------------- + private void writeObject(ObjectOutputStream oos) throws IOException { + oos.defaultWriteObject(); + doWriteObject(oos); + } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + setMap(new HashMap>()); + doReadObject(ois); + } + +} diff --git a/src/main/java/org/apache/commons/collections4/multimap/MultiValuedHashMap.java b/src/main/java/org/apache/commons/collections4/multimap/MultiValuedHashMap.java deleted file mode 100644 index d0a5704a9..000000000 --- a/src/main/java/org/apache/commons/collections4/multimap/MultiValuedHashMap.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * 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.multimap; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.collections4.ListValuedMap; -import org.apache.commons.collections4.MultiValuedMap; -import org.apache.commons.collections4.SetValuedMap; - -/** - * Implements a {@link MultiValuedMap}, using a {@link HashMap} to provide data - * storage. This is the standard implementation of a MultiValuedMap - *

- * A MultiValuedMap is a Map with slightly different semantics. - * Putting a value into the map will add the value to a Collection at that key. - * Getting a value will return a Collection, holding all the values put to that - * key - *

- * In addition, this implementation allows the type of collection used for the - * values to be controlled. By default, an ArrayList is used, - * however a Class to instantiate the value - * collection may be specified. - *

- * Note that MultiValuedHashMap is not synchronized and is not - * thread-safe. If you wish to use this map from multiple threads - * concurrently, you must use appropriate synchronization. This class may throw - * exceptions when accessed by concurrent threads without synchronization. - * - * @since 4.1 - * @version $Id$ - */ -public class MultiValuedHashMap extends AbstractMultiValuedMap { - - /** Serialization Version */ - private static final long serialVersionUID = 20150612L; - - /** - * The initial capacity used when none specified in constructor. - */ - static final int DEFAULT_INITIAL_CAPACITY = 16; - - /** - * The load factor used when none specified in constructor. - */ - static final float DEFAULT_LOAD_FACTOR = 0.75f; - - /** - * Creates a {@link ListValuedMap} with a {@link HashMap} as its internal - * storage - * - * @param the key type - * @param the value type - * @return a new ListValuedMap - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static ListValuedMap listValuedHashMap() { - return new ListValuedHashMap(ArrayList.class); - } - - /** - * Creates a {@link ListValuedMap} with a {@link HashMap} as its internal - * storage which maps the keys to list of type listClass - * - * @param the key type - * @param the value type - * @param the List class type - * @param listClass the class of the list - * @return a new ListValuedMap - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static > ListValuedMap listValuedHashMap(final Class listClass) { - return new ListValuedHashMap(listClass); - } - - /** - * Creates a {@link SetValuedMap} with a {@link HashMap} as its internal - * storage - * - * @param the key type - * @param the value type - * @return a new SetValuedMap - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static SetValuedMap setValuedHashMap() { - return new SetValuedHashMap(HashSet.class); - } - - /** - * Creates a {@link SetValuedMap} with a {@link HashMap} as its internal - * storage which maps the keys to a set of type setClass - * - * @param the key type - * @param the value type - * @param the Set class type - * @param setClass the class of the set - * @return a new SetValuedMap - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static > SetValuedMap setValuedHashMap(final Class setClass) { - return new SetValuedHashMap(setClass); - } - - /** - * Creates a MultiValueMap based on a HashMap with the default - * initial capacity (16) and the default load factor (0.75), which stores - * the multiple values in an ArrayList. - */ - @SuppressWarnings("unchecked") - public MultiValuedHashMap() { - this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, ArrayList.class); - } - - /** - * Creates a MultiValueMap based on a HashMap with the initial - * capacity and the default load factor (0.75), which stores the multiple - * values in an ArrayList. - * - * @param initialCapacity the initial capacity of the underlying hash map - */ - @SuppressWarnings("unchecked") - public MultiValuedHashMap(int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR, ArrayList.class); - } - - /** - * Creates a MultiValueMap based on a HashMap with the initial - * capacity and the load factor, which stores the multiple values in an - * ArrayList. - * - * @param initialCapacity the initial capacity of the underlying hash map - * @param loadFactor the load factor of the underlying hash map - */ - @SuppressWarnings("unchecked") - public MultiValuedHashMap(int initialCapacity, float loadFactor) { - this(initialCapacity, loadFactor, ArrayList.class); - } - - /** - * Creates a MultiValueMap based on a HashMap with the initial - * capacity and the load factor, which stores the multiple values in an - * ArrayList with the initial collection capacity. - * - * @param initialCapacity the initial capacity of the underlying hash map - * @param loadFactor the load factor of the underlying hash map - * @param initialCollectionCapacity the initial capacity of the Collection of values - */ - @SuppressWarnings("unchecked") - public MultiValuedHashMap(int initialCapacity, float loadFactor, int initialCollectionCapacity) { - this(initialCapacity, loadFactor, ArrayList.class, initialCollectionCapacity); - } - - /** - * Creates a MultiValuedHashMap copying all the mappings of the given map. - * - * @param map a MultiValuedMap to copy into this map - */ - @SuppressWarnings("unchecked") - public MultiValuedHashMap(final MultiValuedMap map) { - this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, ArrayList.class); - super.putAll(map); - } - - /** - * Creates a MultiValuedHashMap copying all the mappings of the given map. - * - * @param map a Map to copy into this map - */ - @SuppressWarnings("unchecked") - public MultiValuedHashMap(final Map map) { - this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, ArrayList.class); - super.putAll(map); - } - - /** - * Creates a MultiValuedHashMap which creates the value collections using - * the supplied collectionClazz. - * - * @param initialCapacity the initial capacity of the underlying - * HashMap - * @param loadFactor the load factor of the underlying HashMap - * @param the collection type - * @param collectionClazz the class of the Collection to use to - * create the value collections - */ - protected > MultiValuedHashMap(int initialCapacity, float loadFactor, - final Class collectionClazz) { - super(new HashMap>(initialCapacity, loadFactor), collectionClazz); - } - - /** - * Creates a MultiValuedHashMap which creates the value collections using - * the supplied collectionClazz and the initial collection capacity. - * - * @param the collection type - * @param initialCapacity the initial capacity of the underlying HashMap - * @param loadFactor the load factor of the underlying HashMap - * @param initialCollectionCapacity the initial capacity of the Collection - * @param collectionClazz the class of the Collection to use to create the value collections - */ - protected > MultiValuedHashMap(int initialCapacity, float loadFactor, - final Class collectionClazz, int initialCollectionCapacity) { - super(new HashMap>(initialCapacity, loadFactor), collectionClazz, - initialCollectionCapacity); - } - - /** Inner class for ListValuedMap */ - private static class ListValuedHashMap extends AbstractListValuedMap { - - private static final long serialVersionUID = 20150612L; - - public > ListValuedHashMap(Class listClazz) { - super(new HashMap>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR), listClazz); - } - - public > ListValuedHashMap(Class listClazz, int initialListCapacity) { - super(new HashMap>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR), listClazz, - initialListCapacity); - } - - } - - /** Inner class for SetValuedMap */ - private static class SetValuedHashMap extends AbstractSetValuedMap { - - private static final long serialVersionUID = 20150612L; - - public > SetValuedHashMap(Class setClazz) { - super(new HashMap>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR), setClazz); - } - - public > SetValuedHashMap(Class setClazz, int initialSetCapacity) { - super(new HashMap>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR), setClazz, - initialSetCapacity); - } - - } - -} diff --git a/src/main/java/org/apache/commons/collections4/multimap/TransformedMultiValuedMap.java b/src/main/java/org/apache/commons/collections4/multimap/TransformedMultiValuedMap.java index f0edd332d..93a4a8b38 100644 --- a/src/main/java/org/apache/commons/collections4/multimap/TransformedMultiValuedMap.java +++ b/src/main/java/org/apache/commons/collections4/multimap/TransformedMultiValuedMap.java @@ -91,7 +91,7 @@ public class TransformedMultiValuedMap extends AbstractMultiValuedMapDecor final TransformedMultiValuedMap decorated = new TransformedMultiValuedMap(map, keyTransformer, valueTransformer); if (!map.isEmpty()) { - final MultiValuedMap mapCopy = new MultiValuedHashMap(map); + final MultiValuedMap mapCopy = new ArrayListValuedHashMap(map); decorated.clear(); decorated.putAll(mapCopy); } diff --git a/src/main/java/org/apache/commons/collections4/multimap/package-info.java b/src/main/java/org/apache/commons/collections4/multimap/package-info.java index ace5aa153..030d3fe17 100644 --- a/src/main/java/org/apache/commons/collections4/multimap/package-info.java +++ b/src/main/java/org/apache/commons/collections4/multimap/package-info.java @@ -20,9 +20,8 @@ *

* The following implementations are provided in the package: *

    - *
  • MultiValuedHashMap - implementation that uses a HashMap to store the data - *
  • ListValuedHashMap - implementation of a ListValuedMap using a HashMap as data store - *
  • SetValuedHashMap - implementation of a SetValuedMap using a HashMap as data store + *
  • ArrayListValuedHashMap - ListValuedMap implementation using a HashMap/ArrayList + *
  • HashSetValuedHashMap - SetValuedMap implementation using a HashMap/HashSet *
*

* The following decorators are provided in the package: diff --git a/src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java b/src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java index 865052559..3ce5d9f00 100644 --- a/src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java +++ b/src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java @@ -26,7 +26,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; -import org.apache.commons.collections4.multimap.MultiValuedHashMap; +import org.apache.commons.collections4.multimap.ArrayListValuedHashMap; import org.junit.Test; /** @@ -64,20 +64,20 @@ public class MultiMapUtilsTest { public void testEmptyIfNull() { assertTrue(MultiMapUtils.emptyIfNull(null).isEmpty()); - final MultiValuedMap map = new MultiValuedHashMap(); + final MultiValuedMap map = new ArrayListValuedHashMap(); map.put("item", "value"); assertFalse(MultiMapUtils.emptyIfNull(map).isEmpty()); } @Test public void testIsEmptyWithEmptyMap() { - final MultiValuedMap map = new MultiValuedHashMap(); + final MultiValuedMap map = new ArrayListValuedHashMap(); assertEquals(true, MultiMapUtils.isEmpty(map)); } @Test public void testIsEmptyWithNonEmptyMap() { - final MultiValuedMap map = new MultiValuedHashMap(); + final MultiValuedMap map = new ArrayListValuedHashMap(); map.put("item", "value"); assertEquals(false, MultiMapUtils.isEmpty(map)); } @@ -93,7 +93,7 @@ public class MultiMapUtilsTest { assertNull(MultiMapUtils.getCollection(null, "key1")); String values[] = { "v1", "v2", "v3" }; - final MultiValuedMap map = new MultiValuedHashMap(); + final MultiValuedMap map = new ArrayListValuedHashMap(); for (String val : values) { map.put("key1", val); } @@ -109,7 +109,7 @@ public class MultiMapUtilsTest { assertNull(MultiMapUtils.getValuesAsList(null, "key1")); String values[] = { "v1", "v2", "v3" }; - final MultiValuedMap map = new MultiValuedHashMap(); + final MultiValuedMap map = new ArrayListValuedHashMap(); for (String val : values) { map.put("key1", val); } @@ -126,7 +126,7 @@ public class MultiMapUtilsTest { assertNull(MultiMapUtils.getValuesAsList(null, "key1")); String values[] = { "v1", "v2", "v3" }; - final MultiValuedMap map = new MultiValuedHashMap(); + final MultiValuedMap map = new ArrayListValuedHashMap(); for (String val : values) { map.put("key1", val); map.put("key1", val); @@ -144,7 +144,7 @@ public class MultiMapUtilsTest { assertNull(MultiMapUtils.getValuesAsBag(null, "key1")); String values[] = { "v1", "v2", "v3" }; - final MultiValuedMap map = new MultiValuedHashMap(); + final MultiValuedMap map = new ArrayListValuedHashMap(); for (String val : values) { map.put("key1", val); map.put("key1", val); diff --git a/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java b/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java index 01833b73c..84efedd52 100644 --- a/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java +++ b/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java @@ -33,6 +33,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapIterator; import org.apache.commons.collections4.MultiSet; import org.apache.commons.collections4.MultiValuedMap; +import org.apache.commons.collections4.SetValuedMap; import org.apache.commons.collections4.bag.AbstractBagTest; import org.apache.commons.collections4.bag.HashBag; import org.apache.commons.collections4.collection.AbstractCollectionTest; @@ -104,10 +105,9 @@ public abstract class AbstractMultiValuedMapTest extends AbstractObjectTes return true; } - // FIXME: tests ignore to fix serialization issues @Override public boolean isTestSerialization() { - return false; + return true; } /** @@ -156,13 +156,13 @@ public abstract class AbstractMultiValuedMapTest extends AbstractObjectTes } /** - * Override to return a MultiValuedMap other than MultiValuedHashMap as the - * confirmed map. + * Override to return a MultiValuedMap other than ArrayListValuedHashMap + * as the confirmed map. * * @return a MultiValuedMap that is known to be valid */ public MultiValuedMap makeConfirmedMap() { - return new MultiValuedHashMap(); + return new ArrayListValuedHashMap(); } public MultiValuedMap getConfirmed() { @@ -762,17 +762,15 @@ public abstract class AbstractMultiValuedMapTest extends AbstractObjectTes // extend the AbstractTestMap // ----------------------------------------------------------------------- - // FIXME: tests ignore to fix serialization issues - public void xtestEmptyMapCompatibility() throws Exception { + public void testEmptyMapCompatibility() throws Exception { final MultiValuedMap map = makeObject(); final MultiValuedMap map2 = (MultiValuedMap) readExternalFormFromDisk(getCanonicalEmptyCollectionName(map)); assertEquals("Map is empty", 0, map2.size()); } - // FIXME: tests ignore to fix serialization issues @SuppressWarnings({ "rawtypes", "unchecked" }) - public void xtestFullMapCompatibility() throws Exception { + public void testFullMapCompatibility() throws Exception { final MultiValuedMap map = makeFullMap(); final MultiValuedMap map2 = (MultiValuedMap) readExternalFormFromDisk(getCanonicalFullCollectionName(map)); @@ -1113,10 +1111,12 @@ public abstract class AbstractMultiValuedMapTest extends AbstractObjectTes @Override @SuppressWarnings("unchecked") public Collection[] getSampleValues() { + boolean isSetValuedMap = AbstractMultiValuedMapTest.this.getMap() instanceof SetValuedMap; V[] sampleValues = AbstractMultiValuedMapTest.this.getSampleValues(); Collection[] colArr = new Collection[3]; for(int i = 0; i < 3; i++) { - colArr[i] = Arrays.asList(sampleValues[i*2], sampleValues[i*2 + 1]); + Collection coll = Arrays.asList(sampleValues[i*2], sampleValues[i*2 + 1]); + colArr[i] = isSetValuedMap ? new HashSet(coll) : coll; } return colArr; } @@ -1124,10 +1124,12 @@ public abstract class AbstractMultiValuedMapTest extends AbstractObjectTes @Override @SuppressWarnings("unchecked") public Collection[] getNewSampleValues() { + boolean isSetValuedMap = AbstractMultiValuedMapTest.this.getMap() instanceof SetValuedMap; Object[] sampleValues = { "ein", "ek", "zwei", "duey", "drei", "teen" }; Collection[] colArr = new Collection[3]; for (int i = 0; i < 3; i++) { - colArr[i] = Arrays.asList((V) sampleValues[i * 2], (V) sampleValues[i * 2 + 1]); + Collection coll = Arrays.asList((V) sampleValues[i * 2], (V) sampleValues[i * 2 + 1]); + colArr[i] = isSetValuedMap ? new HashSet(coll) : coll; } return colArr; } diff --git a/src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java b/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java similarity index 52% rename from src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java rename to src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java index 447cce2df..21cf49729 100644 --- a/src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java +++ b/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java @@ -16,95 +16,41 @@ */ package org.apache.commons.collections4.multimap; -import java.util.Iterator; import java.util.List; import java.util.ListIterator; -import java.util.Set; import junit.framework.Test; import org.apache.commons.collections4.BulkTest; import org.apache.commons.collections4.ListValuedMap; import org.apache.commons.collections4.MultiValuedMap; -import org.apache.commons.collections4.SetValuedMap; /** - * Test MultValuedHashMap + * Test ArrayListValuedHashMap * * @since 4.1 * @version $Id$ */ -public class MultiValuedHashMapTest extends AbstractMultiValuedMapTest { +public class ArrayListValuedHashMapTest extends AbstractMultiValuedMapTest { - public MultiValuedHashMapTest(String testName) { + public ArrayListValuedHashMapTest(String testName) { super(testName); } public static Test suite() { - return BulkTest.makeSuite(MultiValuedHashMapTest.class); + return BulkTest.makeSuite(ArrayListValuedHashMapTest.class); } + // ----------------------------------------------------------------------- @Override - public MultiValuedMap makeObject() { - final MultiValuedMap m = new MultiValuedHashMap(); - return m; - } - - @SuppressWarnings("unchecked") - public void testSetValuedMapAdd() { - final SetValuedMap setMap = MultiValuedHashMap.setValuedHashMap(); - assertTrue(setMap.get((K) "whatever") instanceof Set); - - Set set = setMap.get((K) "A"); - assertTrue(set.add((V) "a1")); - assertTrue(set.add((V) "a2")); - assertFalse(set.add((V) "a1")); - assertEquals(2, setMap.size()); - assertTrue(setMap.containsKey("A")); - } - - @SuppressWarnings("unchecked") - public void testSetValuedMapRemove() { - final SetValuedMap setMap = MultiValuedHashMap.setValuedHashMap(); - assertTrue(setMap.get((K) "whatever") instanceof Set); - - Set set = setMap.get((K) "A"); - assertTrue(set.add((V) "a1")); - assertTrue(set.add((V) "a2")); - assertFalse(set.add((V) "a1")); - assertEquals(2, setMap.size()); - assertTrue(setMap.containsKey("A")); - - assertTrue(set.remove("a1")); - assertTrue(set.remove("a2")); - assertFalse(set.remove("a1")); - - assertEquals(0, setMap.size()); - assertFalse(setMap.containsKey("A")); - } - - @SuppressWarnings("unchecked") - public void testSetValuedMapRemoveViaIterator() { - final SetValuedMap setMap = MultiValuedHashMap.setValuedHashMap(); - assertTrue(setMap.get((K) "whatever") instanceof Set); - - Set set = setMap.get((K) "A"); - set.add((V) "a1"); - set.add((V) "a2"); - set.add((V) "a1"); - - Iterator it = set.iterator(); - while (it.hasNext()) { - it.next(); - it.remove(); - } - assertEquals(0, setMap.size()); - assertFalse(setMap.containsKey("A")); + public ListValuedMap makeObject() { + return new ArrayListValuedHashMap(); } + // ----------------------------------------------------------------------- @SuppressWarnings("unchecked") public void testListValuedMapAdd() { - final ListValuedMap listMap = MultiValuedHashMap.listValuedHashMap(); + final ListValuedMap listMap = makeObject(); assertTrue(listMap.get((K) "whatever") instanceof List); List list = listMap.get((K) "A"); list.add((V) "a1"); @@ -114,7 +60,7 @@ public class MultiValuedHashMapTest extends AbstractMultiValuedMapTest listMap = MultiValuedHashMap.listValuedHashMap(); + final ListValuedMap listMap = makeObject(); ListIterator listIt = listMap.get((K) "B").listIterator(); assertFalse(listIt.hasNext()); listIt.add((V) "b1"); @@ -128,7 +74,7 @@ public class MultiValuedHashMapTest extends AbstractMultiValuedMapTest listMap = MultiValuedHashMap.listValuedHashMap(); + final ListValuedMap listMap = makeObject(); List list = listMap.get((K) "A"); list.add((V) "a1"); list.add((V) "a2"); @@ -145,7 +91,7 @@ public class MultiValuedHashMapTest extends AbstractMultiValuedMapTest listMap = MultiValuedHashMap.listValuedHashMap(); + final ListValuedMap listMap = makeObject(); ListIterator listIt = listMap.get((K) "B").listIterator(); listIt.add((V) "b1"); listIt.add((V) "b2"); @@ -165,8 +111,8 @@ public class MultiValuedHashMapTest extends AbstractMultiValuedMapTest extends AbstractMultiValuedMapTest extends AbstractMultiValuedMapTest extends AbstractMultiValuedMapTest { + + public HashSetValuedHashMapTest(String testName) { + super(testName); + } + + public static Test suite() { + return BulkTest.makeSuite(HashSetValuedHashMapTest.class); + } + + // ----------------------------------------------------------------------- + @Override + public SetValuedMap makeObject() { + return new HashSetValuedHashMap(); + } + + @Override + public MultiValuedMap makeConfirmedMap() { + return new HashSetValuedHashMap(); + } + + // ----------------------------------------------------------------------- + @SuppressWarnings("unchecked") + public void testSetValuedMapAdd() { + final SetValuedMap setMap = makeObject(); + assertTrue(setMap.get((K) "whatever") instanceof Set); + + Set set = setMap.get((K) "A"); + assertTrue(set.add((V) "a1")); + assertTrue(set.add((V) "a2")); + assertFalse(set.add((V) "a1")); + assertEquals(2, setMap.size()); + assertTrue(setMap.containsKey("A")); + } + + @SuppressWarnings("unchecked") + public void testSetValuedMapRemove() { + final SetValuedMap setMap = makeObject(); + assertTrue(setMap.get((K) "whatever") instanceof Set); + + Set set = setMap.get((K) "A"); + assertTrue(set.add((V) "a1")); + assertTrue(set.add((V) "a2")); + assertFalse(set.add((V) "a1")); + assertEquals(2, setMap.size()); + assertTrue(setMap.containsKey("A")); + + assertTrue(set.remove("a1")); + assertTrue(set.remove("a2")); + assertFalse(set.remove("a1")); + + assertEquals(0, setMap.size()); + assertFalse(setMap.containsKey("A")); + } + + @SuppressWarnings("unchecked") + public void testSetValuedMapRemoveViaIterator() { + final SetValuedMap setMap = makeObject(); + assertTrue(setMap.get((K) "whatever") instanceof Set); + + Set set = setMap.get((K) "A"); + set.add((V) "a1"); + set.add((V) "a2"); + set.add((V) "a1"); + + Iterator it = set.iterator(); + while (it.hasNext()) { + it.next(); + it.remove(); + } + assertEquals(0, setMap.size()); + assertFalse(setMap.containsKey("A")); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void testSetValuedMapEqualsHashCodeContract() { + SetValuedMap map1 = makeObject(); + SetValuedMap map2 = makeObject(); + + map1.put("a", "a1"); + map1.put("a", "a2"); + map2.put("a", "a2"); + map2.put("a", "a1"); + assertEquals(map1, map2); + assertEquals(map1.hashCode(), map2.hashCode()); + + map2.put("a", "a2"); + assertEquals(map1, map2); + assertEquals(map1.hashCode(), map2.hashCode()); + + map2.put("a", "a3"); + assertNotSame(map1, map2); + assertNotSame(map1.hashCode(), map2.hashCode()); + } + + public void testCreate() throws Exception { + writeExternalFormToDisk((java.io.Serializable) makeObject(), + "src/test/resources/data/test/HashSetValuedHashMap.emptyCollection.version4.1.obj"); + writeExternalFormToDisk((java.io.Serializable) makeFullMap(), + "src/test/resources/data/test/HashSetValuedHashMap.fullCollection.version4.1.obj"); + } + +} diff --git a/src/test/java/org/apache/commons/collections4/multimap/TransformedMultiValuedMapTest.java b/src/test/java/org/apache/commons/collections4/multimap/TransformedMultiValuedMapTest.java index fc7b0ad3a..40da941fc 100644 --- a/src/test/java/org/apache/commons/collections4/multimap/TransformedMultiValuedMapTest.java +++ b/src/test/java/org/apache/commons/collections4/multimap/TransformedMultiValuedMapTest.java @@ -42,18 +42,20 @@ public class TransformedMultiValuedMapTest extends AbstractMultiValuedMapT return BulkTest.makeSuite(TransformedMultiValuedMapTest.class); } + // ----------------------------------------------------------------------- @Override public MultiValuedMap makeObject() { - return TransformedMultiValuedMap.transformingMap(new MultiValuedHashMap(), + return TransformedMultiValuedMap.transformingMap(new ArrayListValuedHashMap(), TransformerUtils. nopTransformer(), TransformerUtils. nopTransformer()); } + // ----------------------------------------------------------------------- @SuppressWarnings("unchecked") public void testKeyTransformedMap() { final Object[] els = new Object[] { "1", "3", "5", "7", "2", "4", "6" }; MultiValuedMap map = TransformedMultiValuedMap.transformingMap( - new MultiValuedHashMap(), + new ArrayListValuedHashMap(), (Transformer) TransformedCollectionTest.STRING_TO_INTEGER_TRANSFORMER, null); assertEquals(0, map.size()); @@ -77,7 +79,7 @@ public class TransformedMultiValuedMapTest extends AbstractMultiValuedMapT final Object[] els = new Object[] { "1", "3", "5", "7", "2", "4", "6" }; MultiValuedMap map = TransformedMultiValuedMap.transformingMap( - new MultiValuedHashMap(), null, + new ArrayListValuedHashMap(), null, (Transformer) TransformedCollectionTest.STRING_TO_INTEGER_TRANSFORMER); assertEquals(0, map.size()); for (int i = 0; i < els.length; i++) { @@ -94,7 +96,7 @@ public class TransformedMultiValuedMapTest extends AbstractMultiValuedMapT // ----------------------------------------------------------------------- @SuppressWarnings("unchecked") public void testFactory_Decorate() { - final MultiValuedMap base = new MultiValuedHashMap(); + final MultiValuedMap base = new ArrayListValuedHashMap(); base.put((K) "A", (V) "1"); base.put((K) "B", (V) "2"); base.put((K) "C", (V) "3"); @@ -114,7 +116,7 @@ public class TransformedMultiValuedMapTest extends AbstractMultiValuedMapT @SuppressWarnings("unchecked") public void testFactory_decorateTransform() { - final MultiValuedMap base = new MultiValuedHashMap(); + final MultiValuedMap base = new ArrayListValuedHashMap(); base.put((K) "A", (V) "1"); base.put((K) "B", (V) "2"); base.put((K) "C", (V) "3"); @@ -132,11 +134,11 @@ public class TransformedMultiValuedMapTest extends AbstractMultiValuedMapT assertEquals(true, trans.get((K) "D").contains(Integer.valueOf(4))); } -// public void testCreate() throws Exception { -// writeExternalFormToDisk((java.io.Serializable) makeObject(), -// "src/test/resources/data/test/TransformedMultiValuedMap.emptyCollection.version4.1.obj"); -// writeExternalFormToDisk((java.io.Serializable) makeFullMap(), -// "src/test/resources/data/test/TransformedMultiValuedMap.fullCollection.version4.1.obj"); -// } + public void testCreate() throws Exception { + writeExternalFormToDisk((java.io.Serializable) makeObject(), + "src/test/resources/data/test/TransformedMultiValuedMap.emptyCollection.version4.1.obj"); + writeExternalFormToDisk((java.io.Serializable) makeFullMap(), + "src/test/resources/data/test/TransformedMultiValuedMap.fullCollection.version4.1.obj"); + } } diff --git a/src/test/java/org/apache/commons/collections4/multimap/UnmodifiableMultiValuedMapTest.java b/src/test/java/org/apache/commons/collections4/multimap/UnmodifiableMultiValuedMapTest.java index c56973299..2841ebfe9 100644 --- a/src/test/java/org/apache/commons/collections4/multimap/UnmodifiableMultiValuedMapTest.java +++ b/src/test/java/org/apache/commons/collections4/multimap/UnmodifiableMultiValuedMapTest.java @@ -47,6 +47,7 @@ public class UnmodifiableMultiValuedMapTest extends AbstractMultiValuedMap return BulkTest.makeSuite(UnmodifiableMultiValuedMapTest.class); } + // ----------------------------------------------------------------------- @Override public boolean isAddSupported() { return false; @@ -59,16 +60,18 @@ public class UnmodifiableMultiValuedMapTest extends AbstractMultiValuedMap @Override public MultiValuedMap makeObject() { - return UnmodifiableMultiValuedMap. unmodifiableMultiValuedMap(new MultiValuedHashMap()); + return UnmodifiableMultiValuedMap. unmodifiableMultiValuedMap( + new ArrayListValuedHashMap()); } @Override protected MultiValuedMap makeFullMap() { - final MultiValuedMap map = new MultiValuedHashMap(); + final MultiValuedMap map = new ArrayListValuedHashMap(); addSampleMappings(map); return UnmodifiableMultiValuedMap. unmodifiableMultiValuedMap(map); } + // ----------------------------------------------------------------------- public void testUnmodifiable() { assertTrue(makeObject() instanceof Unmodifiable); assertTrue(makeFullMap() instanceof Unmodifiable); @@ -259,11 +262,11 @@ public class UnmodifiableMultiValuedMapTest extends AbstractMultiValuedMap } } -// public void testCreate() throws Exception { -// writeExternalFormToDisk((java.io.Serializable) makeObject(), -// "src/test/resources/data/test/UnmodifiableMultiValuedMap.emptyCollection.version4.1.obj"); -// writeExternalFormToDisk((java.io.Serializable) makeFullMap(), -// "src/test/resources/data/test/UnmodifiableMultiValuedMap.fullCollection.version4.1.obj"); -// } + public void testCreate() throws Exception { + writeExternalFormToDisk((java.io.Serializable) makeObject(), + "src/test/resources/data/test/UnmodifiableMultiValuedMap.emptyCollection.version4.1.obj"); + writeExternalFormToDisk((java.io.Serializable) makeFullMap(), + "src/test/resources/data/test/UnmodifiableMultiValuedMap.fullCollection.version4.1.obj"); + } } diff --git a/src/test/resources/data/test/ArrayListValuedHashMap.emptyCollection.version4.1.obj b/src/test/resources/data/test/ArrayListValuedHashMap.emptyCollection.version4.1.obj new file mode 100644 index 0000000000000000000000000000000000000000..a1df83d7323ec727c1d60e597596bbf6860d3dcd GIT binary patch literal 117 zcmZ4UmVvdnh`~O;C|xhHATc>3RWCU|H#a}87)a;jq$ZbS0@)^dxurQJnYoDtdX7a! ziIqN?#U){hIi;y79*M;nzKI14K)`5R?Z?c(=*b|QnU`6TnV17oDq=dPg I1*EV70GIrOYX5#TP+eiio|Vy- z)VVxmU6&GR=C(}DJ3RWCU|H#a}87)a;jq$ZbS0@)^dxurQJnYoDtdLD_z z8NsO~VTn1VsVN|iZ(;!h5HK27`!O>xdNK%Q=4F;-CguPYIfG2hEUByjsbDT=0jaA1 E01<^Gx&QzG literal 0 HcmV?d00001 diff --git a/src/test/resources/data/test/HashSetValuedHashMap.fullCollection.version4.1.obj b/src/test/resources/data/test/HashSetValuedHashMap.fullCollection.version4.1.obj new file mode 100644 index 0000000000000000000000000000000000000000..ec4dce5eb02f4157383a1dd58da86b7bb5ad1aae GIT binary patch literal 191 zcmX|3%L)Q94D7yszz^v~ub#Z=AdyMSy+lmq1LEyCJOBUy literal 0 HcmV?d00001 diff --git a/src/test/resources/data/test/TransformedMultiValuedMap.emptyCollection.version4.1.obj b/src/test/resources/data/test/TransformedMultiValuedMap.emptyCollection.version4.1.obj index 291b6661e26a4d7621e90ad60e16c95fd4b857de..a3135f560f54c8f0a775893bc62bc7490a56998c 100644 GIT binary patch delta 80 zcmdnU{*QTsGvnl5Ms2&IqQpv{%;J);#GKO96pzH>4Bx~81|VQGuJ&VQVDw}V&dkd! e$xO@vsd7#%NKDQwsjL7gVJ>Hx9M2rc$N>N#NrI!!~zB&U^K1_VPasYnC!@?JMoU_ zL@!=Z!Q}j$oYdr!%=|pJ#N?9vqRJ8m&B=U>ifT}S1@;aM+@PT4Ddzx^ M0+ZJ;1~Q5O0MXEbVgLXD diff --git a/src/test/resources/data/test/TransformedMultiValuedMap.fullCollection.version4.1.obj b/src/test/resources/data/test/TransformedMultiValuedMap.fullCollection.version4.1.obj index 46b67e9b6a276c130888f57788f7af9c49695336..2773eb3f960b2cfb3f65fdfed5bf4960b2fec6d4 100644 GIT binary patch delta 162 zcmbQwahhd=Gvnl5M(z5dqQpv{%;J);#GKO96pzH>4Bx~81|VQGuJ&VQVDw}V&dkd! z$xO@vsd7#%NKDQwsjL7gVJ>F@ktIM>o)2a-0of_}#U%_ZDXFCuKu&&MDqNy8FTaF= lsWh*Ifwd%~C>0_h0Mx=#Qj`kR!&*|5pIJQlBC{`}AOPggEMfov literal 1055 zcma)5&2G~`5T4kMCb-}lYze*Ob(C1GF4iH|u(Q|3n^O+}`t zPZAbsPUCxiS|pmMnENA%nK~6RW$_cc_6(DPhL{6At-IyO0VjkjFIZV4rH0!fF$5Ju zuwbCRW(XSaUI@E08@I9c5?({gNI2YB>EJ|ZiIHBG`ItpQq875&+X}%YYZ~sX6}cR% zXL8D2Z(~J`zYkGOmy!nLt-o=k)!u4LRDw>O-2=*kkfnxOYYi@-RDmibJK-75Feo%n z0@Ayo_|bE zKT1%kk!VEG&}l&b?f1rTi65iHbX4YyqFrr9s~{%NXA-*Bi*LR711Yf#xzgs%&%Jlw z@!W;h5In_SF~U++J2iGutM-LoU%y>@{=)_50Jal>ab;-;7xh#!H5Ey0^8Q0x1-Lxl zqIQ=)SJOKe${yeynmcok+HG2m1&Q@yRv1Gwk+Bt+A`=>%A|piGvaG;(n#k?86kHiY g+V8JBNB`^d^ys*8$M(*zKj?p&zjpvkZuQ>y3qv7czyJUM diff --git a/src/test/resources/data/test/UnmodifiableMultiValuedMap.emptyCollection.version4.1.obj b/src/test/resources/data/test/UnmodifiableMultiValuedMap.emptyCollection.version4.1.obj index e76555f329310d2a6c261af8c27548095a06e034..86ea4576d64c8a11666a0d243caf22ce35350dbf 100644 GIT binary patch delta 85 zcmeBSyU8@+HKYCHdyEQ`c^Ele9E*w)D}6GHOTrR!N>fuj5{ol@6AKuCfYG?xkC}nd hlR-E$FS8^wF$bi|Ik6xyIkTj)0;GhwoCTz?0sx@O88rX^ literal 780 zcma))y-ve06oqe_R{Tm(DzPyzvLr~bWhfP>YNb>#6qYXBCT{D>i6h%BDI4#=f&@GP z3y**pm|^4<5buCPgOUm);=#7;0~14Zhs1Qb@AE+Fkul;bs^|Nr zKW2*h7@DVn&pq0w*khy}FPdj4 z0Yh6*uYLi-(7v7(qH_u-UC^lf3ar9=9TCk*s Xg2*9fUKG|2;wr%E#iH)l<7xB-4|@$5 diff --git a/src/test/resources/data/test/UnmodifiableMultiValuedMap.fullCollection.version4.1.obj b/src/test/resources/data/test/UnmodifiableMultiValuedMap.fullCollection.version4.1.obj index c1403154f37163f1ee993c426828e3b9ff23e79e..5efc6df5bfab075af64d0a79470fbb6265664ac4 100644 GIT binary patch delta 163 zcmeyxwv2hgYexIY_ZSr>^DuJOI~ElsR{CTXmxLwel%}S5Bo=4*CKfON0i$uXA2S1^ zCxdWiUS>&VVh%`^b7Dbaa%M?o1xN{VISYs^0iyDJFq;X;PRTDWVPHu~Ev*1@^7B&R n5~X?hB@9fZc_j?2B^gDjU(9%;E|FYc?%F literal 890 zcma))F>ljA6vtm|N6;1lArcz{Ba4j$TZR&~sv5}^41pyJFZMOL>U?MJo|_8;QolnN zB;XUU@DVBoW*GSji0{BNN##_H)L8bN@9yv4`~UC!^)IxYg*{^@F_xH3MVy(UFxqji zl*mZ(`}4S{6v+b1__Z!fE{`RSl{l&=-=L~QeuO3P)p|A`1_0i|;l{-N*pbDIuG02J zW-OAi4Yrj+$QB5mrVLLOc9W%D?i;(wRaVcJoUymDqm{1@(M_*q+ufM7K3fwhNTJgh zKj2+3c24l*#(+y{?{Z7-r6+ibNkvj6?03V&$Ca)tx_F=+p(cqWmgMtqpT0~UM}Gnc zN6?miJ8=Zv(V~n(JxN}UPx$v>2VJ?R(7B;mp%8Rdr2p4@BiNCz&|)#1mx5^zR%r`~ zFUwO4-8xHLXNmjP;yjg({MoNZAAjKI5LyF>oO~}>%ct6Dw9B(<-}-&={o&i6Ap{4o ztqkUiNCViSsTFQ&RPM{&7xgK?o!M<}@ADH9-!tRh!3Vg@<6stXyUnw?VY5*#D(`t@ cv{<95v>^y8&5CwiWsN2xE?zHNBc1pE0NUp!Z~y=R