Applied fixes from Julien Buret to improve MultiMap including
- values() backed by real map - clone works properly - ArrayLists can be added to a MultiMap Javadoc class git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@130874 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
7c12e16836
commit
c8de9e8066
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/MultiHashMap.java,v 1.7 2002/11/01 19:41:00 rwaldhoff Exp $
|
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/MultiHashMap.java,v 1.8 2002/11/24 19:36:48 scolebourne Exp $
|
||||||
* $Revision: 1.7 $
|
* $Revision: 1.8 $
|
||||||
* $Date: 2002/11/01 19:41:00 $
|
* $Date: 2002/11/24 19:36:48 $
|
||||||
*
|
*
|
||||||
* ====================================================================
|
* ====================================================================
|
||||||
*
|
*
|
||||||
|
@ -60,165 +60,333 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.commons.collections;
|
package org.apache.commons.collections;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.util.AbstractCollection;
|
||||||
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.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
/**
|
/**
|
||||||
* <code>MultiHashMap</code> is the default implementation of the
|
* <code>MultiHashMap</code> is the default implementation of the
|
||||||
* {@link org.apache.commons.collections.MultiMap MultiMap} interface.
|
* {@link org.apache.commons.collections.MultiMap MultiMap} interface.
|
||||||
|
* <p>
|
||||||
* A <code>MultiMap</code> is a Map with slightly different semantics.
|
* A <code>MultiMap</code> is a Map with slightly different semantics.
|
||||||
* Instead of returning an Object, it returns a Collection.
|
* Putting a value into the map will add the value to a Collection at that
|
||||||
* So for example, you can put( key, new Integer(1) );
|
* key. Getting a value will always return a Collection, holding all the
|
||||||
* and then a Object get( key ); will return you a Collection
|
* values put to that key. This implementation uses an ArrayList as the
|
||||||
* instead of an Integer.
|
* collection.
|
||||||
|
* <p>
|
||||||
|
* For example:
|
||||||
|
* <pre>
|
||||||
|
* MultiMap mhm = new MultiHashMap();
|
||||||
|
* mhm.put(key, "A");
|
||||||
|
* mhm.put(key, "B");
|
||||||
|
* mhm.put(key, "C");
|
||||||
|
* Collection coll = mhm.get(key);</pre>
|
||||||
|
* <p>
|
||||||
|
* <code>coll</code> will be a list containing "A", "B", "C".
|
||||||
*
|
*
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @author Christopher Berry
|
* @author Christopher Berry
|
||||||
* @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
|
* @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
|
||||||
* @author Steve Downey
|
* @author Steve Downey
|
||||||
* @author Stephen Colebourne
|
* @author Stephen Colebourne
|
||||||
|
* @author <a href="mailto:jburet@yahoo.com">Julien Buret</a>
|
||||||
*/
|
*/
|
||||||
public class MultiHashMap extends HashMap implements MultiMap
|
public class MultiHashMap extends HashMap implements MultiMap {
|
||||||
{
|
// deprecated name concept
|
||||||
//----------------- Data
|
|
||||||
private static int sCount = 0;
|
private static int sCount = 0;
|
||||||
private String mName = null;
|
private String mName = null;
|
||||||
|
|
||||||
public MultiHashMap()
|
//backed values collection
|
||||||
{
|
private transient Collection values = null;
|
||||||
|
|
||||||
|
// compatibility with commons-collection releases 2.0/2.1
|
||||||
|
private static final long serialVersionUID = 1943563828307035349L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public MultiHashMap() {
|
||||||
super();
|
super();
|
||||||
setName();
|
setName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultiHashMap( int initialCapacity )
|
/**
|
||||||
{
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param initialCapacity the initial map capacity
|
||||||
|
*/
|
||||||
|
public MultiHashMap(int initialCapacity) {
|
||||||
super(initialCapacity);
|
super(initialCapacity);
|
||||||
setName();
|
setName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultiHashMap(int initialCapacity, float loadFactor )
|
/**
|
||||||
{
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param initialCapacity the initial map capacity
|
||||||
|
* @param loadFactor the amount 0.0-1.0 at which to resize the map
|
||||||
|
*/
|
||||||
|
public MultiHashMap(int initialCapacity, float loadFactor) {
|
||||||
super(initialCapacity, loadFactor);
|
super(initialCapacity, loadFactor);
|
||||||
setName();
|
setName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultiHashMap( Map mapToCopy )
|
/**
|
||||||
{
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param mapToCopy a Map to copy
|
||||||
|
*/
|
||||||
|
public MultiHashMap(Map mapToCopy) {
|
||||||
super(mapToCopy);
|
super(mapToCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setName()
|
/**
|
||||||
{
|
* Read the object during deserialization.
|
||||||
|
*/
|
||||||
|
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
|
||||||
|
// This method is needed because the 1.2/1.3 Java deserialisation called
|
||||||
|
// put and thus messed up that method
|
||||||
|
|
||||||
|
// default read object
|
||||||
|
s.defaultReadObject();
|
||||||
|
|
||||||
|
// problem only with jvm <1.4
|
||||||
|
String version = "1.2";
|
||||||
|
try {
|
||||||
|
version = System.getProperty("java.version");
|
||||||
|
} catch (SecurityException ex) {
|
||||||
|
// ignore and treat as 1.2/1.3
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.startsWith("1.2") || version.startsWith("1.3")) {
|
||||||
|
for (Iterator iterator = entrySet().iterator(); iterator.hasNext();) {
|
||||||
|
Map.Entry entry = (Map.Entry) iterator.next();
|
||||||
|
// put has created a extra collection level, remove it
|
||||||
|
super.put(entry.getKey(), ((Collection) entry.getValue()).iterator().next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a name for the map.
|
||||||
|
*/
|
||||||
|
private void setName() {
|
||||||
sCount++;
|
sCount++;
|
||||||
mName = "MultiMap-" + sCount;
|
mName = "MultiMap-" + sCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName()
|
/**
|
||||||
{ return mName; }
|
* Gets the name of the map.
|
||||||
|
*
|
||||||
public Object put( Object key, Object value )
|
* @deprecated no replacement. There is no good reason for a MultiMap to have a name
|
||||||
{
|
* @return the name
|
||||||
// NOTE:: put might be called during deserialization !!!!!!
|
*/
|
||||||
// so we must provide a hook to handle this case
|
public String getName() {
|
||||||
// This means that we cannot make MultiMaps of ArrayLists !!!
|
return mName;
|
||||||
|
|
||||||
if ( value instanceof ArrayList ) {
|
|
||||||
return ( super.put( key, value ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList keyList = (ArrayList)(super.get( key ));
|
/**
|
||||||
if ( keyList == null ) {
|
* Put a key and value into the map.
|
||||||
keyList = new ArrayList(10);
|
* <p>
|
||||||
|
* The value is added to a collection mapped to the key instead of
|
||||||
super.put( key, keyList );
|
* replacing the previous value.
|
||||||
|
*
|
||||||
|
* @param key the key to set
|
||||||
|
* @param value the value to set the key to
|
||||||
|
* @return the value added if the add is successful, <code>null</code> otherwise
|
||||||
|
*/
|
||||||
|
public Object put(Object key, Object value) {
|
||||||
|
// NOTE:: put is called during deserialization in JDK < 1.4 !!!!!!
|
||||||
|
// so we must have a readObject()
|
||||||
|
Collection coll = (Collection) super.get(key);
|
||||||
|
if (coll == null) {
|
||||||
|
coll = createCollection(null);
|
||||||
|
super.put(key, coll);
|
||||||
}
|
}
|
||||||
|
boolean results = coll.add(value);
|
||||||
boolean results = keyList.add( value );
|
|
||||||
|
|
||||||
return (results ? value : null);
|
return (results ? value : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsValue( Object value )
|
/**
|
||||||
{
|
* Does the map contain a specific value.
|
||||||
|
* <p>
|
||||||
|
* This searches the collection mapped to each key, and thus could be slow.
|
||||||
|
*
|
||||||
|
* @param value the value to search for
|
||||||
|
* @return true if the list contains the value
|
||||||
|
*/
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
Set pairs = super.entrySet();
|
Set pairs = super.entrySet();
|
||||||
|
|
||||||
if ( pairs == null )
|
if (pairs == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
Iterator pairsIterator = pairs.iterator();
|
Iterator pairsIterator = pairs.iterator();
|
||||||
while (pairsIterator.hasNext()) {
|
while (pairsIterator.hasNext()) {
|
||||||
Map.Entry keyValuePair = (Map.Entry)(pairsIterator.next());
|
Map.Entry keyValuePair = (Map.Entry) pairsIterator.next();
|
||||||
ArrayList list = (ArrayList)(keyValuePair.getValue());
|
Collection coll = (Collection) keyValuePair.getValue();
|
||||||
if( list.contains( value ) )
|
if (coll.contains(value)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object remove( Object key, Object item )
|
/**
|
||||||
{
|
* Removes a specific value from map.
|
||||||
ArrayList valuesForKey = (ArrayList) super.get( key );
|
* <p>
|
||||||
|
* The item is removed from the collection mapped to the specified key.
|
||||||
if ( valuesForKey == null )
|
*
|
||||||
|
* @param key the key to remove from
|
||||||
|
* @param value the value to remove
|
||||||
|
* @return the value removed (which was passed in)
|
||||||
|
*/
|
||||||
|
public Object remove(Object key, Object item) {
|
||||||
|
Collection valuesForKey = (Collection) super.get(key);
|
||||||
|
if (valuesForKey == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
valuesForKey.remove(item);
|
valuesForKey.remove(item);
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear()
|
/**
|
||||||
{
|
* Clear the map.
|
||||||
|
* <p>
|
||||||
|
* This clears each collection in the map, and so may be slow.
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
// For gc, clear each list in the map
|
||||||
Set pairs = super.entrySet();
|
Set pairs = super.entrySet();
|
||||||
Iterator pairsIterator = pairs.iterator();
|
Iterator pairsIterator = pairs.iterator();
|
||||||
while (pairsIterator.hasNext()) {
|
while (pairsIterator.hasNext()) {
|
||||||
Map.Entry keyValuePair = (Map.Entry)(pairsIterator.next());
|
Map.Entry keyValuePair = (Map.Entry) pairsIterator.next();
|
||||||
ArrayList list = (ArrayList)(keyValuePair.getValue());
|
Collection coll = (Collection) keyValuePair.getValue();
|
||||||
list.clear();
|
coll.clear();
|
||||||
}
|
}
|
||||||
super.clear();
|
super.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putAll( Map mapToPut )
|
/**
|
||||||
{
|
* Gets a view over all the values in the map.
|
||||||
super.putAll( mapToPut );
|
* <p>
|
||||||
|
* The values view includes all the entries in the collections at each map key.
|
||||||
|
*
|
||||||
|
* @return the collection view of all the values in the map
|
||||||
|
*/
|
||||||
|
public Collection values() {
|
||||||
|
Collection vs = values;
|
||||||
|
return (vs != null ? vs : (values = new Values()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: Currently the returned {@link Collection} is <i>not</i>
|
* Inner class to view the elements.
|
||||||
* backed by this map.
|
|
||||||
* @see Map#values
|
|
||||||
* @see http://issues.apache.org/bugzilla/show_bug.cgi?id=9573
|
|
||||||
*/
|
*/
|
||||||
public Collection values()
|
private class Values extends AbstractCollection {
|
||||||
{
|
|
||||||
ArrayList returnList = new ArrayList( super.size() );
|
|
||||||
|
|
||||||
Set pairs = super.entrySet();
|
public Iterator iterator() {
|
||||||
Iterator pairsIterator = pairs.iterator();
|
return new ValueIterator();
|
||||||
while ( pairsIterator.hasNext() ) {
|
|
||||||
Map.Entry keyValuePair = (Map.Entry)(pairsIterator.next());
|
|
||||||
ArrayList list = (ArrayList)(keyValuePair.getValue());
|
|
||||||
|
|
||||||
Object[] values = list.toArray();
|
|
||||||
for ( int ii=0; ii < values.length; ii++ ) {
|
|
||||||
returnList.add( values[ii] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return returnList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME:: do we need to implement this??
|
public int size() {
|
||||||
// public boolean equals( Object obj ) {}
|
int compt = 0;
|
||||||
|
Iterator it = iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
compt++;
|
||||||
|
}
|
||||||
|
return compt;
|
||||||
|
}
|
||||||
|
|
||||||
// --------------- From Cloneable
|
public void clear() {
|
||||||
public Object clone()
|
MultiHashMap.this.clear();
|
||||||
{
|
}
|
||||||
MultiHashMap obj = (MultiHashMap)(super.clone());
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner iterator to view the elements.
|
||||||
|
*/
|
||||||
|
private class ValueIterator implements Iterator {
|
||||||
|
private Iterator backedIterator;
|
||||||
|
private Iterator tempIterator;
|
||||||
|
|
||||||
|
private ValueIterator() {
|
||||||
|
backedIterator = MultiHashMap.super.values().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean searchNextIterator() {
|
||||||
|
while (tempIterator == null || tempIterator.hasNext() == false) {
|
||||||
|
if (backedIterator.hasNext() == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tempIterator = ((Collection) backedIterator.next()).iterator();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return searchNextIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object next() {
|
||||||
|
if (searchNextIterator() == false) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
return tempIterator.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
if (tempIterator == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
tempIterator.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone the map.
|
||||||
|
* <p>
|
||||||
|
* The clone will shallow clone the collections as well as the map.
|
||||||
|
*
|
||||||
|
* @return the cloned map
|
||||||
|
*/
|
||||||
|
public Object clone() {
|
||||||
|
MultiHashMap obj = (MultiHashMap) super.clone();
|
||||||
obj.mName = mName;
|
obj.mName = mName;
|
||||||
|
|
||||||
|
// clone each Collection container
|
||||||
|
for (Iterator it = entrySet().iterator(); it.hasNext();) {
|
||||||
|
Map.Entry entry = (Map.Entry) it.next();
|
||||||
|
Collection coll = (Collection) entry.getValue();
|
||||||
|
Collection newColl = createCollection(coll);
|
||||||
|
entry.setValue(newColl);
|
||||||
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of the map value Collection container.
|
||||||
|
* <p>
|
||||||
|
* This method can be overridden to use your own collection type.
|
||||||
|
*
|
||||||
|
* @param coll the collection to copy, may be null
|
||||||
|
* @return the new collection
|
||||||
|
*/
|
||||||
|
protected Collection createCollection(Collection coll) {
|
||||||
|
if (coll == null) {
|
||||||
|
return new ArrayList();
|
||||||
|
} else {
|
||||||
|
return new ArrayList(coll);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue