mirror of
https://github.com/apache/commons-collections.git
synced 2025-02-16 06:56:05 +00:00
[COLLECTIONS-508] Added asMap() implementation for MultiValuedMaps.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1715695 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
dd3ac288b6
commit
d323621656
@ -17,6 +17,7 @@
|
||||
package org.apache.commons.collections4;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
@ -289,8 +290,18 @@ public interface MultiValuedMap<K, V> {
|
||||
Collection<V> values();
|
||||
|
||||
/**
|
||||
* Returns a {@link Map} view of this MultiValuedMap with a Collection as
|
||||
* its value. The Collection holds all the values mapped to that key.
|
||||
* Returns a view of this multi-valued map as a {@code Map} from each distinct
|
||||
* key to the non-empty collection of that key's associated values.
|
||||
* <p>
|
||||
* Note that {@code this.asMap().get(k)} is equivalent to {@code this.get(k)}
|
||||
* only when {@code k} is a key contained in the multi-valued map; otherwise it
|
||||
* returns {@code null} as opposed to an empty collection.
|
||||
* <p>
|
||||
* Changes to the returned map or the collections that serve as its values
|
||||
* will update the underlying multi-valued map, and vice versa. The map does
|
||||
* not support {@code put} or {@code putAll}, nor do its entries support
|
||||
* {@link Map.Entry#setValue setValue} and iterators support
|
||||
* {@link Iterator#remove remove}.
|
||||
*
|
||||
* @return a map view of the mappings in this multi-valued map
|
||||
*/
|
||||
|
@ -79,6 +79,11 @@ public abstract class AbstractListValuedMap<K, V> extends AbstractMultiValuedMap
|
||||
*/
|
||||
@Override
|
||||
public List<V> get(final K key) {
|
||||
return wrappedCollection(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
List<V> wrappedCollection(final K key) {
|
||||
return new WrappedList(key);
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,8 @@ import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
@ -33,11 +35,13 @@ 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.iterators.AbstractIteratorDecorator;
|
||||
import org.apache.commons.collections4.iterators.EmptyMapIterator;
|
||||
import org.apache.commons.collections4.iterators.IteratorChain;
|
||||
import org.apache.commons.collections4.iterators.LazyIteratorChain;
|
||||
import org.apache.commons.collections4.iterators.TransformIterator;
|
||||
import org.apache.commons.collections4.keyvalue.AbstractMapEntry;
|
||||
import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry;
|
||||
import org.apache.commons.collections4.multiset.AbstractMultiSet;
|
||||
import org.apache.commons.collections4.multiset.UnmodifiableMultiSet;
|
||||
|
||||
@ -61,6 +65,9 @@ public abstract class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K,
|
||||
/** The KeyMultiSet view */
|
||||
private transient MultiSet<K> keysMultiSetView;
|
||||
|
||||
/** The AsMap view */
|
||||
private transient AsMap asMapView;
|
||||
|
||||
/** The map used to store the data */
|
||||
private transient Map<K, Collection<V>> map;
|
||||
|
||||
@ -140,6 +147,10 @@ public abstract class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K,
|
||||
*/
|
||||
@Override
|
||||
public Collection<V> get(final K key) {
|
||||
return wrappedCollection(key);
|
||||
}
|
||||
|
||||
Collection<V> wrappedCollection(final K key) {
|
||||
return new WrappedCollection(key);
|
||||
}
|
||||
|
||||
@ -324,10 +335,8 @@ public abstract class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K,
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<K, Collection<V>> asMap() {
|
||||
// TODO: return a view of the map
|
||||
return (Map<K, Collection<V>>) getMap();
|
||||
return asMapView != null ? asMapView : (asMapView = new AsMap(map));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -767,6 +776,131 @@ public abstract class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class that provides the AsMap view.
|
||||
*/
|
||||
private class AsMap extends AbstractMap<K, Collection<V>> {
|
||||
final transient Map<K, Collection<V>> decoratedMap;
|
||||
|
||||
AsMap(final Map<K, Collection<V>> map) {
|
||||
this.decoratedMap = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<K, Collection<V>>> entrySet() {
|
||||
return new AsMapEntrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return decoratedMap.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> get(Object key) {
|
||||
Collection<V> collection = decoratedMap.get(key);
|
||||
if (collection == null) {
|
||||
return null;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
K k = (K) key;
|
||||
return wrappedCollection(k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return AbstractMultiValuedMap.this.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return decoratedMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> remove(Object key) {
|
||||
Collection<V> collection = decoratedMap.remove(key);
|
||||
if (collection == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Collection<V> output = createCollection();
|
||||
output.addAll(collection);
|
||||
collection.clear();
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
return this == object || decoratedMap.equals(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return decoratedMap.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return decoratedMap.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
AbstractMultiValuedMap.this.clear();
|
||||
}
|
||||
|
||||
class AsMapEntrySet extends AbstractSet<Map.Entry<K, Collection<V>>> {
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<K, Collection<V>>> iterator() {
|
||||
return new AsMapEntrySetIterator(decoratedMap.entrySet().iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return AsMap.this.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
AsMap.this.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return decoratedMap.entrySet().contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
if (!contains(o)) {
|
||||
return false;
|
||||
}
|
||||
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
|
||||
AbstractMultiValuedMap.this.remove(entry.getKey());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EntrySet iterator for the asMap view.
|
||||
*/
|
||||
class AsMapEntrySetIterator extends AbstractIteratorDecorator<Map.Entry<K, Collection<V>>> {
|
||||
|
||||
AsMapEntrySetIterator(Iterator<Map.Entry<K, Collection<V>>> iterator) {
|
||||
super(iterator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<K, Collection<V>> next() {
|
||||
final Map.Entry<K, Collection<V>> entry = super.next();
|
||||
final K key = entry.getKey();
|
||||
return new UnmodifiableMapEntry<K, Collection<V>>(key, wrappedCollection(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Write the map out using a custom routine.
|
||||
|
@ -78,6 +78,11 @@ public abstract class AbstractSetValuedMap<K, V> extends AbstractMultiValuedMap<
|
||||
*/
|
||||
@Override
|
||||
public Set<V> get(final K key) {
|
||||
return wrappedCollection(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<V> wrappedCollection(final K key) {
|
||||
return new WrappedSet(key);
|
||||
}
|
||||
|
||||
|
@ -287,6 +287,10 @@ public abstract class AbstractMapTest<K, V> extends AbstractObjectTest {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean areEqualElementsDistinguishable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of keys in the mappings used to test the map. This
|
||||
* method must return an array with the same length as {@link
|
||||
@ -1587,6 +1591,11 @@ public abstract class AbstractMapTest<K, V> extends AbstractObjectTest {
|
||||
return AbstractMapTest.this.isGetStructuralModify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqualElementsDistinguishable() {
|
||||
return AbstractMapTest.this.areEqualElementsDistinguishable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTestSerialization() {
|
||||
return false;
|
||||
|
@ -683,30 +683,6 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
|
||||
assertTrue(col.contains("uno"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testAsMapPut() {
|
||||
if (!isAddSupported()) {
|
||||
return;
|
||||
}
|
||||
resetEmpty();
|
||||
Map<K, Collection<V>> mapCol = getMap().asMap();
|
||||
Collection<V> col = (Collection<V>) Arrays.asList("un", "uno");
|
||||
mapCol.put((K) "one", col);
|
||||
assertEquals(2, getMap().size());
|
||||
assertTrue(getMap().containsKey("one"));
|
||||
assertTrue(getMap().containsValue("un"));
|
||||
assertTrue(getMap().containsValue("uno"));
|
||||
|
||||
resetFull();
|
||||
mapCol = getMap().asMap();
|
||||
col = mapCol.get("one");
|
||||
col.add((V) "one");
|
||||
assertEquals(7, getMap().size());
|
||||
assertTrue(getMap().containsValue("one"));
|
||||
assertTrue(getMap().containsValue("un"));
|
||||
assertTrue(getMap().containsValue("uno"));
|
||||
}
|
||||
|
||||
public void testAsMapRemove() {
|
||||
if (!isRemoveSupported()) {
|
||||
return;
|
||||
@ -1141,23 +1117,31 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
|
||||
|
||||
@Override
|
||||
public boolean isPutAddSupported() {
|
||||
return AbstractMultiValuedMapTest.this.isAddSupported();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPutChangeSupported() {
|
||||
return AbstractMultiValuedMapTest.this.isAddSupported();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRemoveSupported() {
|
||||
return AbstractMultiValuedMapTest.this.isRemoveSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqualElementsDistinguishable() {
|
||||
// work-around for a problem with the EntrySet: the entries contain
|
||||
// the wrapped collection, which will be automatically cleared
|
||||
// when the associated key is removed from the map as the collection
|
||||
// is not cached atm.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTestSerialization() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user