[COLLECTIONS-508] Further additions.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1588354 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
bc85bab7a5
commit
0721f49bf0
|
@ -35,15 +35,14 @@ public interface ListValuedMap<K, V> extends MultiValuedMap<K, V> {
|
||||||
/**
|
/**
|
||||||
* Gets the list of values associated with the specified key.
|
* Gets the list of values associated with the specified key.
|
||||||
* <p>
|
* <p>
|
||||||
* Implementations typically return <code>null</code> if no values have been
|
* This method will return an <b>empty</b> list if
|
||||||
* mapped to the key, however the implementation may choose to return an
|
* {@link #containsKey(Object)} returns {@code false}. Changes to the
|
||||||
* empty collection.
|
* returned list will update the underlying {@code ListValuedMap} and
|
||||||
* <p>
|
* vice-versa.
|
||||||
* Implementations may choose to return a clone of the internal collection.
|
|
||||||
*
|
*
|
||||||
* @param key the key to retrieve
|
* @param key the key to retrieve
|
||||||
* @return the <code>Collection</code> of values, implementations should
|
* @return the <code>List</code> of values, implementations should return an
|
||||||
* return <code>null</code> for no mapping, but may return an empty collection
|
* empty list for no mapping
|
||||||
* @throws ClassCastException if the key is of an invalid type
|
* @throws ClassCastException if the key is of an invalid type
|
||||||
* @throws NullPointerException if the key is null and null keys are invalid
|
* @throws NullPointerException if the key is null and null keys are invalid
|
||||||
*/
|
*/
|
||||||
|
@ -52,14 +51,13 @@ public interface ListValuedMap<K, V> extends MultiValuedMap<K, V> {
|
||||||
/**
|
/**
|
||||||
* Removes all values associated with the specified key.
|
* Removes all values associated with the specified key.
|
||||||
* <p>
|
* <p>
|
||||||
* Implementations typically return <code>null</code> from a subsequent
|
* The returned list <i>may</i> be modifiable, but updates will not be
|
||||||
* <code>get(Object)</code>, however they may choose to return an empty
|
* propagated to this list-valued map. In case no mapping was stored for the
|
||||||
* collection.
|
* specified key, an empty, unmodifiable list will be returned.
|
||||||
*
|
*
|
||||||
* @param key the key to remove values from
|
* @param key the key to remove values from
|
||||||
* @return the <code>Collection</code> of values removed, implementations
|
* @return the <code>List</code> of values removed, implementations
|
||||||
* should return <code>null</code> for no mapping found, but may
|
* typically return an empty, unmodifiable List for no mapping found
|
||||||
* return an empty collection
|
|
||||||
* @throws UnsupportedOperationException if the map is unmodifiable
|
* @throws UnsupportedOperationException if the map is unmodifiable
|
||||||
* @throws ClassCastException if the key is of an invalid type
|
* @throws ClassCastException if the key is of an invalid type
|
||||||
* @throws NullPointerException if the key is null and null keys are invalid
|
* @throws NullPointerException if the key is null and null keys are invalid
|
||||||
|
|
|
@ -0,0 +1,290 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
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.TransformedMultiValuedMap;
|
||||||
|
import org.apache.commons.collections4.multimap.UnmodifiableMultiValuedMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides utility methods and decorators for {@link MultiValuedMap} instances.
|
||||||
|
* <p>
|
||||||
|
* It contains various type safe and null safe methods.
|
||||||
|
* <p>
|
||||||
|
* It also provides the following decorators:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #unmodifiableMultiValuedMap(MultiValuedMap)}</li>
|
||||||
|
* <li>{@link #transformedMultiValuedMap(MultiValuedMap, Transformer, Transformer)}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @since 4.1
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class MultiMapUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>MultiMapUtils</code> should not normally be instantiated.
|
||||||
|
*/
|
||||||
|
private MultiMapUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An empty {@link UnmodifiableMultiValuedMap}.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
public static final MultiValuedMap EMPTY_MULTI_VALUED_MAP =
|
||||||
|
UnmodifiableMultiValuedMap.unmodifiableMultiValuedMap(new MultiValuedHashMap());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns immutable EMPTY_MULTI_VALUED_MAP with generic type safety.
|
||||||
|
*
|
||||||
|
* @param <K> the type of key in the map
|
||||||
|
* @param <V> the type of value in the map
|
||||||
|
* @return immutable and empty <code>MultiValuedMap</code>
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <K, V> MultiValuedMap<K, V> emptyMultiValuedMap() {
|
||||||
|
return EMPTY_MULTI_VALUED_MAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Null safe methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an immutable empty <code>MultiValuedMap</code> if the argument is
|
||||||
|
* <code>null</code>, or the argument itself otherwise.
|
||||||
|
*
|
||||||
|
* @param <K> the type of key in the map
|
||||||
|
* @param <V> the type of value in the map
|
||||||
|
* @param map the map, possibly <code>null</code>
|
||||||
|
* @return an empty <code>MultiValuedMap</code> if the argument is <code>null</code>
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <K, V> MultiValuedMap<K, V> emptyIfNull(final MultiValuedMap<K, V> map) {
|
||||||
|
return map == null ? EMPTY_MULTI_VALUED_MAP : map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Null-safe check if the specified <code>MultiValuedMap</code> is empty.
|
||||||
|
* <p>
|
||||||
|
* Null returns true.
|
||||||
|
*
|
||||||
|
* @param map the map to check, may be null
|
||||||
|
* @return true if empty or null
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(final MultiValuedMap<?, ?> map) {
|
||||||
|
return map == null || map.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Null safe getters
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a Collection from <code>MultiValuedMap</code> in a null-safe manner.
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @param map the <code>MultiValuedMap</code> to use
|
||||||
|
* @param key the key to look up
|
||||||
|
* @return the Collection in the <code>MultiValuedMap</code>, <code>null</code> if map input is null
|
||||||
|
*/
|
||||||
|
public static <K, V> Collection<V> getCollection(final MultiValuedMap<K, V> map, final K key) {
|
||||||
|
if (map != null) {
|
||||||
|
return map.get(key);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a List from <code>MultiValuedMap</code> in a null-safe manner.
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @param map the <code>MultiValuedMap</code> to use
|
||||||
|
* @param key the key to look up
|
||||||
|
* @return the Collection in the <code>MultiValuedMap</code> as List,
|
||||||
|
* <code>null</code> if map input is null
|
||||||
|
*/
|
||||||
|
public static <K, V> List<V> getList(MultiValuedMap<K, V> map, K key) {
|
||||||
|
if (map != null) {
|
||||||
|
Collection<V> col = map.get(key);
|
||||||
|
if (col instanceof List) {
|
||||||
|
return (List<V>) col;
|
||||||
|
}
|
||||||
|
return new ArrayList<V>(col);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a Set from <code>MultiValuedMap</code> in a null-safe manner.
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @param map the <code>MultiValuedMap</code> to use
|
||||||
|
* @param key the key to look up
|
||||||
|
* @return the Collection in the <code>MultiValuedMap</code> as Set,
|
||||||
|
* <code>null</code> if map input is null
|
||||||
|
*/
|
||||||
|
public static <K, V> Set<V> getSet(MultiValuedMap<K, V> map, K key) {
|
||||||
|
if (map != null) {
|
||||||
|
Collection<V> col = map.get(key);
|
||||||
|
if (col instanceof Set) {
|
||||||
|
return (Set<V>) col;
|
||||||
|
}
|
||||||
|
return new HashSet<V>(col);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a Bag from <code>MultiValuedMap</code> in a null-safe manner.
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @param map the <code>MultiValuedMap</code> to use
|
||||||
|
* @param key the key to look up
|
||||||
|
* @return the Collection in the <code>MultiValuedMap</code> as Bag,
|
||||||
|
* <code>null</code> if map input is null
|
||||||
|
*/
|
||||||
|
public static <K, V> Bag<V> getBag(MultiValuedMap<K, V> map, K key) {
|
||||||
|
if (map != null) {
|
||||||
|
Collection<V> col = map.get(key);
|
||||||
|
if (col instanceof Bag) {
|
||||||
|
return (Bag<V>) col;
|
||||||
|
}
|
||||||
|
return new HashBag<V>(col);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory Methods
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link ListValuedMap} with a {@link HashMap} as its internal storage.
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @return a new <code>ListValuedMap</code>
|
||||||
|
*/
|
||||||
|
public static <K, V> ListValuedMap<K, V> createListValuedHashMap() {
|
||||||
|
return MultiValuedHashMap.<K, V>listValuedHashMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link ListValuedMap} with a {@link HashMap} as its internal
|
||||||
|
* storage which maps the keys to list of type <code>listClass</code>.
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @param <C> the List class type
|
||||||
|
* @param listClass the class of the list
|
||||||
|
* @return a new <code>ListValuedMap</code>
|
||||||
|
*/
|
||||||
|
public static <K, V, C extends List<V>> ListValuedMap<K, V> createListValuedHashMap(final Class<C> listClass) {
|
||||||
|
return MultiValuedHashMap.<K, V, C>listValuedHashMap(listClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link SetValuedMap} with a {@link HashMap} as its internal
|
||||||
|
* storage
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @return a new <code>SetValuedMap</code>
|
||||||
|
*/
|
||||||
|
public static <K, V> SetValuedMap<K, V> createSetValuedHashMap() {
|
||||||
|
return MultiValuedHashMap.<K, V>setValuedHashMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link SetValuedMap} with a {@link HashMap} as its internal
|
||||||
|
* storage which maps the keys to a set of type <code>setClass</code>
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @param <C> the Set class type
|
||||||
|
* @param setClass the class of the set
|
||||||
|
* @return a new <code>SetValuedMap</code>
|
||||||
|
*/
|
||||||
|
public static <K, V, C extends Set<V>> SetValuedMap<K, V> createSetValuedHashMap(final Class<C> setClass) {
|
||||||
|
return MultiValuedHashMap.<K, V, C>setValuedHashMap(setClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiValuedMap Decorators
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an <code>UnmodifiableMultiValuedMap</code> backed by the given
|
||||||
|
* map.
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @param map the <code>MultiValuedMap</code> to make unmodifiable, must not
|
||||||
|
* be null
|
||||||
|
* @return an <code>UnmodifiableMultiValuedMap</code> backed by the given
|
||||||
|
* map
|
||||||
|
* @throws IllegalArgumentException if the map is null
|
||||||
|
*/
|
||||||
|
public static <K, V> MultiValuedMap<K, V> unmodifiableMultiValuedMap(
|
||||||
|
final MultiValuedMap<? extends K, ? extends V> map) {
|
||||||
|
return UnmodifiableMultiValuedMap.<K, V>unmodifiableMultiValuedMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a <code>TransformedMultiValuedMap</code> backed by the given map.
|
||||||
|
* <p>
|
||||||
|
* This method returns a new <code>MultiValuedMap</code> (decorating the
|
||||||
|
* specified map) that will transform any new entries added to it. Existing
|
||||||
|
* entries in the specified map will not be transformed. If you want that
|
||||||
|
* behaviour, see {@link TransformedMultiValuedMap#transformedMap}.
|
||||||
|
* <p>
|
||||||
|
* Each object is passed through the transformers as it is added to the Map.
|
||||||
|
* It is important not to use the original map after invoking this method,
|
||||||
|
* as it is a back door for adding untransformed objects.
|
||||||
|
* <p>
|
||||||
|
* If there are any elements already in the map being decorated, they are
|
||||||
|
* NOT transformed.
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @param map the <code>MultiValuedMap</code> to transform, must not be
|
||||||
|
* null, typically empty
|
||||||
|
* @param keyTransformer the transformer for the map keys, null means no
|
||||||
|
* transformation
|
||||||
|
* @param valueTransformer the transformer for the map values, null means no
|
||||||
|
* transformation
|
||||||
|
* @return a transformed <code>MultiValuedMap</code> backed by the given map
|
||||||
|
* @throws IllegalArgumentException if the <code>MultiValuedMap</code> is
|
||||||
|
* null
|
||||||
|
*/
|
||||||
|
public static <K, V> MultiValuedMap<K, V> transformedMultiValuedMap(final MultiValuedMap<K, V> map,
|
||||||
|
final Transformer<? super K, ? extends K> keyTransformer,
|
||||||
|
final Transformer<? super V, ? extends V> valueTransformer) {
|
||||||
|
return TransformedMultiValuedMap.transformingMap(map, keyTransformer, valueTransformer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -102,15 +102,17 @@ public interface MultiValuedMap<K, V> {
|
||||||
boolean containsMapping(Object key, Object value);
|
boolean containsMapping(Object key, Object value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a view collection of the values associated with the specified key.
|
* Returns a view collection of the values associated with the specified
|
||||||
|
* key.
|
||||||
* <p>
|
* <p>
|
||||||
* This method will return an <b>empty</b> collection if {@link #containsKey(Object)}
|
* This method will return an <b>empty</b> collection if
|
||||||
* returns {@code false}. Changes to the returned collection will update the
|
* {@link #containsKey(Object)} returns {@code false}. Changes to the
|
||||||
* underlying {@code MultiValuedMap} and vice-versa.
|
* returned collection will update the underlying {@code MultiValuedMap} and
|
||||||
|
* vice-versa.
|
||||||
*
|
*
|
||||||
* @param key the key to retrieve
|
* @param key the key to retrieve
|
||||||
* @return the <code>Collection</code> of values, implementations should
|
* @return the <code>Collection</code> of values, implementations should
|
||||||
* return <code>null</code> for no mapping, but may return an empty collection
|
* return an empty collection for no mapping
|
||||||
* @throws ClassCastException if the key is of an invalid type (optional)
|
* @throws ClassCastException if the key is of an invalid type (optional)
|
||||||
* @throws NullPointerException if the key is null and null keys are invalid (optional)
|
* @throws NullPointerException if the key is null and null keys are invalid (optional)
|
||||||
*/
|
*/
|
||||||
|
@ -220,8 +222,7 @@ public interface MultiValuedMap<K, V> {
|
||||||
* Other values attached to that key are unaffected.
|
* Other values attached to that key are unaffected.
|
||||||
* <p>
|
* <p>
|
||||||
* If the last value for a key is removed, implementations typically return
|
* If the last value for a key is removed, implementations typically return
|
||||||
* <code>null</code> from a subsequent <code>get(Object)</code>, however
|
* an empty collection from a subsequent <code>get(Object)</code>.
|
||||||
* they may choose to return an empty collection.
|
|
||||||
*
|
*
|
||||||
* @param key the key to remove from
|
* @param key the key to remove from
|
||||||
* @param item the item to remove
|
* @param item the item to remove
|
||||||
|
|
|
@ -37,16 +37,13 @@ public interface SetValuedMap<K, V> extends MultiValuedMap<K, V> {
|
||||||
/**
|
/**
|
||||||
* Gets the set of values associated with the specified key.
|
* Gets the set of values associated with the specified key.
|
||||||
* <p>
|
* <p>
|
||||||
* Implementations typically return <code>null</code> if no values have been
|
* Implementations typically return an empty <code>Set</code> if no values
|
||||||
* mapped to the key, however the implementation may choose to return an
|
* have been mapped to the key.
|
||||||
* empty collection.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Implementations may choose to return a clone of the internal collection.
|
|
||||||
*
|
*
|
||||||
* @param key the key to retrieve
|
* @param key the key to retrieve
|
||||||
* @return the <code>Set</code> of values, implementations should return
|
* @return the <code>Set</code> of values, implementations should return an
|
||||||
* <code>null</code> for no mapping, but may return an empty
|
* empty Set for no mapping
|
||||||
* collection
|
|
||||||
* @throws ClassCastException if the key is of an invalid type
|
* @throws ClassCastException if the key is of an invalid type
|
||||||
* @throws NullPointerException if the key is null and null keys are invalid
|
* @throws NullPointerException if the key is null and null keys are invalid
|
||||||
*/
|
*/
|
||||||
|
@ -55,9 +52,9 @@ public interface SetValuedMap<K, V> extends MultiValuedMap<K, V> {
|
||||||
/**
|
/**
|
||||||
* Removes all values associated with the specified key.
|
* Removes all values associated with the specified key.
|
||||||
* <p>
|
* <p>
|
||||||
* Implementations typically return <code>null</code> from a subsequent
|
* The returned set <i>may</i> be modifiable, but updates will not be
|
||||||
* <code>get(Object)</code>, however they may choose to return an empty
|
* propagated to this set-valued map. In case no mapping was stored for the
|
||||||
* collection.
|
* specified key, an empty, unmodifiable set will be returned.
|
||||||
*
|
*
|
||||||
* @param key the key to remove values from
|
* @param key the key to remove values from
|
||||||
* @return the <code>Set</code> of values removed, implementations should
|
* @return the <code>Set</code> of values removed, implementations should
|
||||||
|
|
|
@ -0,0 +1,246 @@
|
||||||
|
/*
|
||||||
|
* 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.io.Serializable;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.ListUtils;
|
||||||
|
import org.apache.commons.collections4.ListValuedMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract implementation of the {@link ListValuedMap} interface to simplify
|
||||||
|
* the creation of subclass implementations.
|
||||||
|
* <p>
|
||||||
|
* Subclasses specify a Map implementation to use as the internal storage and
|
||||||
|
* the List implementation to use as values.
|
||||||
|
*
|
||||||
|
* @since 4.1
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public abstract class AbstractListValuedMap<K, V> extends AbstractMultiValuedMap<K, V>
|
||||||
|
implements ListValuedMap<K, V>, Serializable {
|
||||||
|
|
||||||
|
/** The serialization version */
|
||||||
|
private static final long serialVersionUID = 6024950625989666915L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constructor that wraps, not copies
|
||||||
|
*
|
||||||
|
* @param <C> the list type
|
||||||
|
* @param map the map to wrap, must not be null
|
||||||
|
* @param listClazz the collection class
|
||||||
|
* @throws IllegalArgumentException if the map is null
|
||||||
|
*/
|
||||||
|
protected <C extends List<V>> AbstractListValuedMap(Map<K, ? super C> map, Class<C> listClazz) {
|
||||||
|
super(map, listClazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constructor that wraps, not copies
|
||||||
|
*
|
||||||
|
* @param <C> 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 IllegalArgumentException if the map is null or if
|
||||||
|
* initialListCapacity is negative
|
||||||
|
*/
|
||||||
|
protected <C extends List<V>> AbstractListValuedMap(Map<K, ? super C> map, Class<C> listClazz,
|
||||||
|
int initialListCapacity) {
|
||||||
|
super(map, listClazz, initialListCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of values associated with the specified key. This would
|
||||||
|
* return an empty list in case the mapping is not present
|
||||||
|
*
|
||||||
|
* @param key the key to retrieve
|
||||||
|
* @return the <code>List</code> of values, will return an empty
|
||||||
|
* <code>List</code> for no mapping
|
||||||
|
* @throws ClassCastException if the key is of an invalid type
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<V> get(Object key) {
|
||||||
|
return new WrappedList(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all values associated with the specified key.
|
||||||
|
* <p>
|
||||||
|
* A subsequent <code>get(Object)</code> would return an empty list.
|
||||||
|
*
|
||||||
|
* @param key the key to remove values from
|
||||||
|
* @return the <code>List</code> of values removed, will return an empty,
|
||||||
|
* unmodifiable list for no mapping found.
|
||||||
|
* @throws ClassCastException if the key is of an invalid type
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<V> remove(Object key) {
|
||||||
|
return ListUtils.emptyIfNull((List<V>) getMap().remove(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapped list to handle add and remove on the list returned by get(object)
|
||||||
|
*/
|
||||||
|
private class WrappedList extends WrappedCollection implements List<V> {
|
||||||
|
|
||||||
|
public WrappedList(Object key) {
|
||||||
|
super(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void add(int index, V value) {
|
||||||
|
List<V> list = (List<V>) getMapping();
|
||||||
|
if (list == null) {
|
||||||
|
list = (List<V>) AbstractListValuedMap.this.createCollection();
|
||||||
|
list.add(index, value);
|
||||||
|
getMap().put((K) key, list);
|
||||||
|
}
|
||||||
|
list.add(index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public boolean addAll(int index, Collection<? extends V> c) {
|
||||||
|
List<V> list = (List<V>) getMapping();
|
||||||
|
if (list == null) {
|
||||||
|
list = (List<V>) createCollection();
|
||||||
|
boolean result = list.addAll(index, c);
|
||||||
|
if (result) {
|
||||||
|
getMap().put((K) key, list);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return list.addAll(index, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public V get(int index) {
|
||||||
|
final List<V> list = ListUtils.emptyIfNull((List<V>) getMapping());
|
||||||
|
return list.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int indexOf(Object o) {
|
||||||
|
final List<V> list = ListUtils.emptyIfNull((List<V>) getMapping());
|
||||||
|
return list.indexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int lastIndexOf(Object o) {
|
||||||
|
final List<V> list = ListUtils.emptyIfNull((List<V>) getMapping());
|
||||||
|
return list.indexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListIterator<V> listIterator() {
|
||||||
|
return new ValuesListIterator(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListIterator<V> listIterator(int index) {
|
||||||
|
return new ValuesListIterator(key, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public V remove(int index) {
|
||||||
|
final List<V> list = ListUtils.emptyIfNull((List<V>) getMapping());
|
||||||
|
V value = list.remove(index);
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
AbstractListValuedMap.this.remove(key);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public V set(int index, V value) {
|
||||||
|
final List<V> list = ListUtils.emptyIfNull((List<V>) getMapping());
|
||||||
|
return list.set(index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<V> subList(int fromIndex, int toIndex) {
|
||||||
|
final List<V> list = ListUtils.emptyIfNull((List<V>) getMapping());
|
||||||
|
return list.subList(fromIndex, toIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Values ListItrerator */
|
||||||
|
private class ValuesListIterator implements ListIterator<V>{
|
||||||
|
|
||||||
|
private final Object key;
|
||||||
|
|
||||||
|
private List<V> values;
|
||||||
|
private ListIterator<V> iterator;
|
||||||
|
|
||||||
|
public ValuesListIterator(Object key){
|
||||||
|
this.key = key;
|
||||||
|
this.values = ListUtils.emptyIfNull((List<V>) getMap().get(key));
|
||||||
|
this.iterator = values.listIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValuesListIterator(Object key, int index){
|
||||||
|
this.key = key;
|
||||||
|
this.values = ListUtils.emptyIfNull((List<V>) getMap().get(key));
|
||||||
|
this.iterator = values.listIterator(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void add(V value) {
|
||||||
|
if (getMap().get(key) == null) {
|
||||||
|
List<V> list = (List<V>) createCollection();
|
||||||
|
getMap().put((K) key, list);
|
||||||
|
this.values = list;
|
||||||
|
this.iterator = list.listIterator();
|
||||||
|
}
|
||||||
|
this.iterator.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return iterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasPrevious() {
|
||||||
|
return iterator.hasPrevious();
|
||||||
|
}
|
||||||
|
|
||||||
|
public V next() {
|
||||||
|
return iterator.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int nextIndex() {
|
||||||
|
return iterator.nextIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public V previous() {
|
||||||
|
return iterator.previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int previousIndex() {
|
||||||
|
return iterator.previousIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
iterator.remove();
|
||||||
|
if (values.isEmpty()) {
|
||||||
|
getMap().remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(V value) {
|
||||||
|
iterator.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,7 +21,6 @@ import java.lang.reflect.Array;
|
||||||
import java.util.AbstractCollection;
|
import java.util.AbstractCollection;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
@ -95,14 +94,14 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
|
||||||
*
|
*
|
||||||
* @param <C> the collection type
|
* @param <C> the collection type
|
||||||
* @param map the map to wrap, must not be null
|
* @param map the map to wrap, must not be null
|
||||||
* @param initialCollectionCapacity the initial capacity of the collection
|
|
||||||
* @param collectionClazz the collection class
|
* @param collectionClazz the collection class
|
||||||
|
* @param initialCollectionCapacity the initial capacity of the collection
|
||||||
* @throws IllegalArgumentException if the map is null or if
|
* @throws IllegalArgumentException if the map is null or if
|
||||||
* initialCollectionCapacity is negetive
|
* initialCollectionCapacity is negative
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected <C extends Collection<V>> AbstractMultiValuedMap(final Map<K, ? super C> map,
|
protected <C extends Collection<V>> AbstractMultiValuedMap(final Map<K, ? super C> map,
|
||||||
int initialCollectionCapacity, final Class<C> collectionClazz) {
|
final Class<C> collectionClazz, final int initialCollectionCapacity) {
|
||||||
if (map == null) {
|
if (map == null) {
|
||||||
throw new IllegalArgumentException("Map must not be null");
|
throw new IllegalArgumentException("Map must not be null");
|
||||||
}
|
}
|
||||||
|
@ -179,16 +178,15 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
|
||||||
/**
|
/**
|
||||||
* Removes all values associated with the specified key.
|
* Removes all values associated with the specified key.
|
||||||
* <p>
|
* <p>
|
||||||
* A subsequent <code>get(Object)</code> would return null collection.
|
* A subsequent <code>get(Object)</code> would return an empty collection.
|
||||||
*
|
*
|
||||||
* @param key the key to remove values from
|
* @param key the key to remove values from
|
||||||
* @return the <code>Collection</code> of values removed, will return
|
* @return the <code>Collection</code> of values removed, will return an
|
||||||
* <code>null</code> for no mapping found.
|
* empty, unmodifiable collection for no mapping found.
|
||||||
* @throws ClassCastException if the key is of an invalid type
|
* @throws ClassCastException if the key is of an invalid type
|
||||||
*/
|
*/
|
||||||
public Collection<V> remove(Object key) {
|
public Collection<V> remove(Object key) {
|
||||||
Collection<V> coll = getMap().remove(key);
|
return CollectionUtils.emptyIfNull(getMap().remove(key));
|
||||||
return coll == null ? Collections.<V>emptyList() : coll;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -197,7 +195,7 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
|
||||||
* The item is removed from the collection mapped to the specified key.
|
* The item is removed from the collection mapped to the specified key.
|
||||||
* Other values attached to that key are unaffected.
|
* Other values attached to that key are unaffected.
|
||||||
* <p>
|
* <p>
|
||||||
* If the last value for a key is removed, <code>null</code> would be
|
* If the last value for a key is removed, an empty collection would be
|
||||||
* returned from a subsequent <code>get(Object)</code>.
|
* returned from a subsequent <code>get(Object)</code>.
|
||||||
*
|
*
|
||||||
* @param key the key to remove from
|
* @param key the key to remove from
|
||||||
|
@ -455,15 +453,15 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
|
||||||
/**
|
/**
|
||||||
* Wrapped collection to handle add and remove on the collection returned by get(object)
|
* Wrapped collection to handle add and remove on the collection returned by get(object)
|
||||||
*/
|
*/
|
||||||
private class WrappedCollection implements Collection<V> {
|
protected class WrappedCollection implements Collection<V> {
|
||||||
|
|
||||||
private final Object key;
|
protected final Object key;
|
||||||
|
|
||||||
public WrappedCollection(Object key) {
|
public WrappedCollection(Object key) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<V> getMapping() {
|
protected Collection<V> getMapping() {
|
||||||
return getMap().get(key);
|
return getMap().get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* 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.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.SetUtils;
|
||||||
|
import org.apache.commons.collections4.SetValuedMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract implementation of the {@link SetValuedMap} interface to simplify the
|
||||||
|
* creation of subclass implementations.
|
||||||
|
* <p>
|
||||||
|
* Subclasses specify a Map implementation to use as the internal storage and
|
||||||
|
* the Set implementation to use as values.
|
||||||
|
*
|
||||||
|
* @since 4.1
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public abstract class AbstractSetValuedMap<K, V> extends AbstractMultiValuedMap<K, V> implements SetValuedMap<K, V> {
|
||||||
|
|
||||||
|
/** Serialization version */
|
||||||
|
private static final long serialVersionUID = 3383617478898639862L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constructor that wraps, not copies
|
||||||
|
*
|
||||||
|
* @param <C> the set type
|
||||||
|
* @param map the map to wrap, must not be null
|
||||||
|
* @param setClazz the collection class
|
||||||
|
* @throws IllegalArgumentException if the map is null
|
||||||
|
*/
|
||||||
|
protected <C extends Set<V>> AbstractSetValuedMap(Map<K, ? super C> map, Class<C> setClazz) {
|
||||||
|
super(map, setClazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constructor that wraps, not copies
|
||||||
|
*
|
||||||
|
* @param <C> 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 IllegalArgumentException if the map is null or if
|
||||||
|
* initialSetCapacity is negative
|
||||||
|
*/
|
||||||
|
protected <C extends Set<V>> AbstractSetValuedMap(Map<K, ? super C> map, Class<C> setClazz,
|
||||||
|
int initialSetCapacity) {
|
||||||
|
super(map, setClazz, initialSetCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the set of values associated with the specified key. This would
|
||||||
|
* return an empty set in case the mapping is not present
|
||||||
|
*
|
||||||
|
* @param key the key to retrieve
|
||||||
|
* @return the <code>Set</code> of values, will return an empty
|
||||||
|
* <code>Set</code> for no mapping
|
||||||
|
* @throws ClassCastException if the key is of an invalid type
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Set<V> get(Object key) {
|
||||||
|
return new WrappedSet(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all values associated with the specified key.
|
||||||
|
* <p>
|
||||||
|
* A subsequent <code>get(Object)</code> would return an empty set.
|
||||||
|
*
|
||||||
|
* @param key the key to remove values from
|
||||||
|
* @return the <code>Set</code> of values removed, will return an empty,
|
||||||
|
* unmodifiable set for no mapping found.
|
||||||
|
* @throws ClassCastException if the key is of an invalid type
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Set<V> remove(Object key) {
|
||||||
|
return SetUtils.emptyIfNull((Set<V>) getMap().remove(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapped set to handle add and remove on the collection returned by
|
||||||
|
* get(object)
|
||||||
|
*/
|
||||||
|
protected class WrappedSet extends WrappedCollection implements Set<V> {
|
||||||
|
|
||||||
|
public WrappedSet(Object key) {
|
||||||
|
super(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,9 +19,14 @@ package org.apache.commons.collections4.multimap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
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.MultiValuedMap;
|
||||||
|
import org.apache.commons.collections4.SetValuedMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a {@link MultiValuedMap}, using a {@link HashMap} to provide data
|
* Implements a {@link MultiValuedMap}, using a {@link HashMap} to provide data
|
||||||
|
@ -61,18 +66,59 @@ public class MultiValuedHashMap<K, V> extends AbstractMultiValuedMap<K, V> imple
|
||||||
static final float DEFAULT_LOAD_FACTOR = 0.75f;
|
static final float DEFAULT_LOAD_FACTOR = 0.75f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a MultiValuedHashMap which maps keys to collections of type
|
* Creates a {@link ListValuedMap} with a {@link HashMap} as its internal
|
||||||
* <code>collectionClass</code>.
|
* storage
|
||||||
*
|
*
|
||||||
* @param <K> the key type
|
* @param <K> the key type
|
||||||
* @param <V> the value type
|
* @param <V> the value type
|
||||||
* @param <C> the collection class type
|
* @return a new <code>ListValuedMap</code>
|
||||||
* @param collectionClass the type of the collection class
|
|
||||||
* @return a new MultiValuedMap
|
|
||||||
*/
|
*/
|
||||||
public static <K, V, C extends Collection<V>> MultiValuedMap<K, V> multiValuedMap(
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
final Class<C> collectionClass) {
|
public static <K, V> ListValuedMap<K, V> listValuedHashMap() {
|
||||||
return new MultiValuedHashMap<K, V>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, collectionClass);
|
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 <code>listClass</code>
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @param <C> the List class type
|
||||||
|
* @param listClass the class of the list
|
||||||
|
* @return a new <code>ListValuedMap</code>
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
public static <K, V, C extends List<V>> ListValuedMap<K, V> listValuedHashMap(final Class<C> listClass) {
|
||||||
|
return new ListValuedHashMap(listClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link SetValuedMap} with a {@link HashMap} as its internal
|
||||||
|
* storage
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @return a new <code>SetValuedMap</code>
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
public static <K, V> SetValuedMap<K, V> 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 <code>setClass</code>
|
||||||
|
*
|
||||||
|
* @param <K> the key type
|
||||||
|
* @param <V> the value type
|
||||||
|
* @param <C> the Set class type
|
||||||
|
* @param setClass the class of the set
|
||||||
|
* @return a new <code>SetValuedMap</code>
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
public static <K, V, C extends Set<V>> SetValuedMap<K, V> setValuedHashMap(final Class<C> setClass) {
|
||||||
|
return new SetValuedHashMap(setClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -122,7 +168,7 @@ public class MultiValuedHashMap<K, V> extends AbstractMultiValuedMap<K, V> imple
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public MultiValuedHashMap(int initialCapacity, float loadFactor, int initialCollectionCapacity) {
|
public MultiValuedHashMap(int initialCapacity, float loadFactor, int initialCollectionCapacity) {
|
||||||
this(initialCapacity, loadFactor, initialCollectionCapacity, ArrayList.class);
|
this(initialCapacity, loadFactor, ArrayList.class, initialCollectionCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -178,8 +224,40 @@ public class MultiValuedHashMap<K, V> extends AbstractMultiValuedMap<K, V> imple
|
||||||
* create the value collections
|
* create the value collections
|
||||||
*/
|
*/
|
||||||
protected <C extends Collection<V>> MultiValuedHashMap(int initialCapacity, float loadFactor,
|
protected <C extends Collection<V>> MultiValuedHashMap(int initialCapacity, float loadFactor,
|
||||||
int initialCollectionCapacity, final Class<C> collectionClazz) {
|
final Class<C> collectionClazz, int initialCollectionCapacity) {
|
||||||
super(new HashMap<K, Collection<V>>(initialCapacity, loadFactor), initialCollectionCapacity, collectionClazz);
|
super(new HashMap<K, Collection<V>>(initialCapacity, loadFactor), collectionClazz, initialCollectionCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Inner class for ListValuedMap */
|
||||||
|
private static class ListValuedHashMap<K, V> extends AbstractListValuedMap<K, V> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 3667581458573135234L;
|
||||||
|
|
||||||
|
public <C extends List<V>> ListValuedHashMap(Class<C> listClazz) {
|
||||||
|
super(new HashMap<K, List<V>>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR), listClazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <C extends List<V>> ListValuedHashMap(Class<C> listClazz, int initialListCapacity) {
|
||||||
|
super(new HashMap<K, List<V>>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR), listClazz,
|
||||||
|
initialListCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Inner class for SetValuedMap */
|
||||||
|
private static class SetValuedHashMap<K, V> extends AbstractSetValuedMap<K, V> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -3817515514829894543L;
|
||||||
|
|
||||||
|
public <C extends Set<V>> SetValuedHashMap(Class<C> setClazz) {
|
||||||
|
super(new HashMap<K, Set<V>>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR), setClazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <C extends Set<V>> SetValuedHashMap(Class<C> setClazz, int initialSetCapacity) {
|
||||||
|
super(new HashMap<K, Set<V>>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR), setClazz,
|
||||||
|
initialSetCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.multimap.MultiValuedHashMap;
|
||||||
|
|
||||||
|
import junit.framework.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for MultiMapUtils
|
||||||
|
*
|
||||||
|
* @since 4.1
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class MultiMapUtilsTest extends BulkTest {
|
||||||
|
|
||||||
|
public static Test suite() {
|
||||||
|
return BulkTest.makeSuite(MultiMapUtilsTest.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultiMapUtilsTest(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
public void testEmptyUnmodifiableMultiValuedMap() {
|
||||||
|
final MultiValuedMap map = MultiMapUtils.EMPTY_MULTI_VALUED_MAP;
|
||||||
|
assertTrue(map.isEmpty());
|
||||||
|
try {
|
||||||
|
map.put("key", "value");
|
||||||
|
fail("Should throw UnsupportedOperationException");
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testTypeSafeEmptyMultiValuedMap() {
|
||||||
|
final MultiValuedMap<String, String> map = MultiMapUtils.<String, String>emptyMultiValuedMap();
|
||||||
|
assertTrue(map.isEmpty());
|
||||||
|
try {
|
||||||
|
map.put("key", "value");
|
||||||
|
fail("Should throw UnsupportedOperationException");
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEmptyIfNull() {
|
||||||
|
assertTrue(MultiMapUtils.emptyIfNull(null).isEmpty());
|
||||||
|
|
||||||
|
final MultiValuedMap<String, String> map = new MultiValuedHashMap<String, String>();
|
||||||
|
map.put("item", "value");
|
||||||
|
assertFalse(MultiMapUtils.emptyIfNull(map).isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsEmptyWithEmptyMap() {
|
||||||
|
final MultiValuedMap<Object, Object> map = new MultiValuedHashMap<Object, Object>();
|
||||||
|
assertEquals(true, MultiMapUtils.isEmpty(map));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsEmptyWithNonEmptyMap() {
|
||||||
|
final MultiValuedMap<String, String> map = new MultiValuedHashMap<String, String>();
|
||||||
|
map.put("item", "value");
|
||||||
|
assertEquals(false, MultiMapUtils.isEmpty(map));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsEmptyWithNull() {
|
||||||
|
final MultiValuedMap<Object, Object> map = null;
|
||||||
|
assertEquals(true, MultiMapUtils.isEmpty(map));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetCollection() {
|
||||||
|
assertNull(MultiMapUtils.getCollection(null, "key1"));
|
||||||
|
|
||||||
|
String values[] = { "v1", "v2", "v3" };
|
||||||
|
final MultiValuedMap<String, String> map = new MultiValuedHashMap<String, String>();
|
||||||
|
for (String val : values) {
|
||||||
|
map.put("key1", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<String> col = MultiMapUtils.getCollection(map, "key1");
|
||||||
|
for (String val : values) {
|
||||||
|
assertTrue(col.contains(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetList() {
|
||||||
|
assertNull(MultiMapUtils.getList(null, "key1"));
|
||||||
|
|
||||||
|
String values[] = { "v1", "v2", "v3" };
|
||||||
|
final MultiValuedMap<String, String> map = new MultiValuedHashMap<String, String>();
|
||||||
|
for (String val : values) {
|
||||||
|
map.put("key1", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> list = MultiMapUtils.getList(map, "key1");
|
||||||
|
int i = 0;
|
||||||
|
for (String val : list) {
|
||||||
|
assertTrue(val.equals(values[i++]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetSet() {
|
||||||
|
assertNull(MultiMapUtils.getList(null, "key1"));
|
||||||
|
|
||||||
|
String values[] = { "v1", "v2", "v3" };
|
||||||
|
final MultiValuedMap<String, String> map = new MultiValuedHashMap<String, String>();
|
||||||
|
for (String val : values) {
|
||||||
|
map.put("key1", val);
|
||||||
|
map.put("key1", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> set = MultiMapUtils.getSet(map, "key1");
|
||||||
|
assertEquals(3, set.size());
|
||||||
|
for (String val : values) {
|
||||||
|
assertTrue(set.contains(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetBag() {
|
||||||
|
assertNull(MultiMapUtils.getBag(null, "key1"));
|
||||||
|
|
||||||
|
String values[] = { "v1", "v2", "v3" };
|
||||||
|
final MultiValuedMap<String, String> map = new MultiValuedHashMap<String, String>();
|
||||||
|
for (String val : values) {
|
||||||
|
map.put("key1", val);
|
||||||
|
map.put("key1", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bag<String> bag = MultiMapUtils.getBag(map, "key1");
|
||||||
|
assertEquals(6, bag.size());
|
||||||
|
for (String val : values) {
|
||||||
|
assertTrue(bag.contains(val));
|
||||||
|
assertEquals(2, bag.getCount(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,13 +16,17 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.commons.collections4.multimap;
|
package org.apache.commons.collections4.multimap;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Iterator;
|
||||||
import java.util.HashSet;
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import junit.framework.Test;
|
import junit.framework.Test;
|
||||||
|
|
||||||
import org.apache.commons.collections4.BulkTest;
|
import org.apache.commons.collections4.BulkTest;
|
||||||
|
import org.apache.commons.collections4.ListValuedMap;
|
||||||
import org.apache.commons.collections4.MultiValuedMap;
|
import org.apache.commons.collections4.MultiValuedMap;
|
||||||
|
import org.apache.commons.collections4.SetValuedMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test MultValuedHashMap
|
* Test MultValuedHashMap
|
||||||
|
@ -46,41 +50,117 @@ public class MultiValuedHashMapTest<K, V> extends AbstractMultiValuedMapTest<K,
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*private <C extends Collection<V>> MultiValuedHashMap<K, V> createTestMap(final Class<C> collectionClass) {
|
@SuppressWarnings("unchecked")
|
||||||
final MultiValuedHashMap<K, V> map =
|
public void testSetValuedMapAdd() {
|
||||||
(MultiValuedHashMap<K, V>) MultiValuedHashMap.<K, V, C> multiValuedMap(collectionClass);
|
final SetValuedMap<K, V> setMap = MultiValuedHashMap.setValuedHashMap();
|
||||||
addSampleMappings(map);
|
assertTrue(setMap.get("whatever") instanceof Set);
|
||||||
return map;
|
|
||||||
|
Set<V> set = setMap.get("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")
|
@SuppressWarnings("unchecked")
|
||||||
public void testValueCollectionType() {
|
public void testSetValuedMapRemove() {
|
||||||
final MultiValuedHashMap<K, V> map = createTestMap(LinkedList.class);
|
final SetValuedMap<K, V> setMap = MultiValuedHashMap.setValuedHashMap();
|
||||||
assertTrue(map.get("one") instanceof LinkedList);
|
assertTrue(setMap.get("whatever") instanceof Set);
|
||||||
}*/
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
Set<V> set = setMap.get("A");
|
||||||
public void testPutWithList() {
|
assertTrue(set.add((V) "a1"));
|
||||||
final MultiValuedHashMap<K, V> test =
|
assertTrue(set.add((V) "a2"));
|
||||||
(MultiValuedHashMap<K, V>) MultiValuedHashMap.multiValuedMap(ArrayList.class);
|
assertFalse(set.add((V) "a1"));
|
||||||
assertEquals(true, test.put((K) "A", (V) "a"));
|
assertEquals(2, setMap.size());
|
||||||
assertEquals(true, test.put((K) "A", (V) "b"));
|
assertTrue(setMap.containsKey("A"));
|
||||||
assertEquals(true, test.put((K) "A", (V) "a"));
|
|
||||||
assertEquals(1, test.keySet().size());
|
assertTrue(set.remove("a1"));
|
||||||
assertEquals(3, test.get("A").size());
|
assertTrue(set.remove("a2"));
|
||||||
assertEquals(3, test.size());
|
assertFalse(set.remove("a1"));
|
||||||
|
|
||||||
|
assertEquals(0, setMap.size());
|
||||||
|
assertFalse(setMap.containsKey("A"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void testPutWithSet() {
|
public void testSetValuedMapRemoveViaIterator() {
|
||||||
final MultiValuedHashMap<K, V> test =
|
final SetValuedMap<K, V> setMap = MultiValuedHashMap.setValuedHashMap();
|
||||||
(MultiValuedHashMap<K, V>) MultiValuedHashMap.multiValuedMap(HashSet.class);
|
assertTrue(setMap.get("whatever") instanceof Set);
|
||||||
assertEquals(true, test.put((K) "A", (V) "a"));
|
|
||||||
assertEquals(true, test.put((K) "A", (V) "b"));
|
Set<V> set = setMap.get("A");
|
||||||
assertEquals(false, test.put((K) "A", (V) "a"));
|
set.add((V) "a1");
|
||||||
assertEquals(1, test.keySet().size());
|
set.add((V) "a2");
|
||||||
assertEquals(2, test.get("A").size());
|
set.add((V) "a1");
|
||||||
assertEquals(2, test.size());
|
|
||||||
|
Iterator<V> it = set.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
assertEquals(0, setMap.size());
|
||||||
|
assertFalse(setMap.containsKey("A"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void testListValuedMapAdd() {
|
||||||
|
final ListValuedMap<K, V> listMap = MultiValuedHashMap.listValuedHashMap();
|
||||||
|
assertTrue(listMap.get("whatever") instanceof List);
|
||||||
|
List<V> list = listMap.get("A");
|
||||||
|
list.add((V) "a1");
|
||||||
|
assertEquals(1, listMap.size());
|
||||||
|
assertTrue(listMap.containsKey("A"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void testListValuedMapAddViaListIterator() {
|
||||||
|
final ListValuedMap<K, V> listMap = MultiValuedHashMap.listValuedHashMap();
|
||||||
|
ListIterator<V> listIt = listMap.get("B").listIterator();
|
||||||
|
assertFalse(listIt.hasNext());
|
||||||
|
listIt.add((V) "b1");
|
||||||
|
listIt.add((V) "b2");
|
||||||
|
listIt.add((V) "b3");
|
||||||
|
assertEquals(3, listMap.size());
|
||||||
|
assertTrue(listMap.containsKey("B"));
|
||||||
|
// As ListIterator always adds before the current cursor
|
||||||
|
assertFalse(listIt.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void testListValuedMapRemove() {
|
||||||
|
final ListValuedMap<K, V> listMap = MultiValuedHashMap.listValuedHashMap();
|
||||||
|
List<V> list = listMap.get("A");
|
||||||
|
list.add((V) "a1");
|
||||||
|
list.add((V) "a2");
|
||||||
|
list.add((V) "a3");
|
||||||
|
assertEquals(3, listMap.size());
|
||||||
|
assertEquals("a1", list.remove(0));
|
||||||
|
assertEquals(2, listMap.size());
|
||||||
|
assertEquals("a2", list.remove(0));
|
||||||
|
assertEquals(1, listMap.size());
|
||||||
|
assertEquals("a3", list.remove(0));
|
||||||
|
assertEquals(0, listMap.size());
|
||||||
|
assertFalse(listMap.containsKey("A"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void testListValuedMapRemoveViaListIterator() {
|
||||||
|
final ListValuedMap<K, V> listMap = MultiValuedHashMap.listValuedHashMap();
|
||||||
|
ListIterator<V> listIt = listMap.get("B").listIterator();
|
||||||
|
listIt.add((V) "b1");
|
||||||
|
listIt.add((V) "b2");
|
||||||
|
assertEquals(2, listMap.size());
|
||||||
|
assertTrue(listMap.containsKey("B"));
|
||||||
|
listIt = listMap.get("B").listIterator();
|
||||||
|
while (listIt.hasNext()) {
|
||||||
|
listIt.next();
|
||||||
|
listIt.remove();
|
||||||
|
}
|
||||||
|
assertFalse(listMap.containsKey("B"));
|
||||||
|
listIt.add((V) "b1");
|
||||||
|
listIt.add((V) "b2");
|
||||||
|
assertTrue(listMap.containsKey("B"));
|
||||||
|
assertEquals(2, listMap.get("B").size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// public void testCreate() throws Exception {
|
// public void testCreate() throws Exception {
|
||||||
|
|
Loading…
Reference in New Issue