Change MultiKeyMap to be a decorator
git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@131689 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
39ea52a450
commit
c1576044be
Binary file not shown.
Binary file not shown.
|
@ -15,12 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.commons.collections.map;
|
package org.apache.commons.collections.map;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.collections.IterableMap;
|
||||||
import org.apache.commons.collections.MapIterator;
|
import org.apache.commons.collections.MapIterator;
|
||||||
import org.apache.commons.collections.keyvalue.MultiKey;
|
import org.apache.commons.collections.keyvalue.MultiKey;
|
||||||
|
|
||||||
|
@ -33,14 +34,29 @@ import org.apache.commons.collections.keyvalue.MultiKey;
|
||||||
* <code>remove</code> for individual keys which operate without extra object creation.
|
* <code>remove</code> for individual keys which operate without extra object creation.
|
||||||
* <p>
|
* <p>
|
||||||
* The additional methods are the main interface of this map.
|
* The additional methods are the main interface of this map.
|
||||||
* As such, you will not mormally hold this map in a variable of type <code>Map</code>.
|
* As such, you will not normally hold this map in a variable of type <code>Map</code>.
|
||||||
* The normal map methods take in and return a {@link MultiKey}.
|
|
||||||
* <p>
|
* <p>
|
||||||
* As an example, consider a cache that uses a String airline code and a Locale
|
* The normal map methods take in and return a {@link MultiKey}.
|
||||||
* to lookup the airline's name:
|
* If you try to use <code>put()</code> with any other object type a
|
||||||
|
* <code>ClassCastException</code> is thrown. If you try to use <code>null</code> as
|
||||||
|
* the key in <code>put()</code> a <code>NullPointerException</code> is thrown.
|
||||||
|
* <p>
|
||||||
|
* This map is implemented as a decorator of a <code>AbstractHashedMap</code> which
|
||||||
|
* enables extra behaviour to be added easily.
|
||||||
|
* <ul>
|
||||||
|
* <li><code>MultiKeyMap.decorate(new LinkedMap())</code> creates an ordered map.
|
||||||
|
* <li><code>MultiKeyMap.decorate(new LRUMap())</code> creates an least recently used map.
|
||||||
|
* <li><code>MultiKeyMap.decorate(new ReferenceMap())</code> creates a garbage collector sensitive map.
|
||||||
|
* </ul>
|
||||||
|
* Note that <code>IdentityMap</code> and <code>ReferenceIdentityMap</code> are unsuitable
|
||||||
|
* for use as the key comparison would work on the whole MultiKey, not the elements within.
|
||||||
|
* <p>
|
||||||
|
* As an example, consider a least recently used cache that uses a String airline code
|
||||||
|
* and a Locale to lookup the airline's name:
|
||||||
* <pre>
|
* <pre>
|
||||||
|
* private MultiKeyMap cache = MultiKeyMap.decorate(new LRUMap(50));
|
||||||
|
*
|
||||||
* public String getAirlineName(String code, String locale) {
|
* public String getAirlineName(String code, String locale) {
|
||||||
* MultiKeyMap cache = getCache();
|
|
||||||
* String name = (String) cache.get(code, locale);
|
* String name = (String) cache.get(code, locale);
|
||||||
* if (name == null) {
|
* if (name == null) {
|
||||||
* name = getAirlineNameFromDB(code, locale);
|
* name = getAirlineNameFromDB(code, locale);
|
||||||
|
@ -51,80 +67,56 @@ import org.apache.commons.collections.keyvalue.MultiKey;
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @since Commons Collections 3.1
|
* @since Commons Collections 3.1
|
||||||
* @version $Revision: 1.1 $ $Date: 2004/04/12 12:05:30 $
|
* @version $Revision: 1.2 $ $Date: 2004/04/30 23:51:36 $
|
||||||
*
|
*
|
||||||
* @author Stephen Colebourne
|
* @author Stephen Colebourne
|
||||||
*/
|
*/
|
||||||
public class MultiKeyMap
|
public class MultiKeyMap
|
||||||
extends AbstractHashedMap implements Serializable, Cloneable {
|
implements IterableMap, Serializable {
|
||||||
|
|
||||||
/** Serialisation version */
|
/** Serialisation version */
|
||||||
private static final long serialVersionUID = -1788199231038721040L;
|
private static final long serialVersionUID = -1788199231038721040L;
|
||||||
|
|
||||||
/**
|
/** The decorated map */
|
||||||
* Constructs a new empty map with default size and load factor.
|
protected final AbstractHashedMap map;
|
||||||
*/
|
|
||||||
public MultiKeyMap() {
|
|
||||||
super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Constructs a new, empty map with the specified initial capacity.
|
* Decorates the specified map to add the MultiKeyMap API and fast query.
|
||||||
|
* The map must not be null and must be empty.
|
||||||
*
|
*
|
||||||
* @param initialCapacity the initial capacity
|
* @param map the map to decorate, not null
|
||||||
* @throws IllegalArgumentException if the initial capacity is less than one
|
* @throws IllegalArgumentException if the map is null or not empty
|
||||||
*/
|
*/
|
||||||
public MultiKeyMap(int initialCapacity) {
|
public static MultiKeyMap decorate(AbstractHashedMap map) {
|
||||||
super(initialCapacity);
|
if (map == null) {
|
||||||
|
throw new IllegalArgumentException("Map must not be null");
|
||||||
}
|
}
|
||||||
|
if (map.size() > 0) {
|
||||||
/**
|
throw new IllegalArgumentException("Map must be empty");
|
||||||
* Constructs a new, empty map with the specified initial capacity and
|
|
||||||
* load factor.
|
|
||||||
*
|
|
||||||
* @param initialCapacity the initial capacity
|
|
||||||
* @param loadFactor the load factor
|
|
||||||
* @throws IllegalArgumentException if the initial capacity is less than one
|
|
||||||
* @throws IllegalArgumentException if the load factor is less than zero
|
|
||||||
*/
|
|
||||||
public MultiKeyMap(int initialCapacity, float loadFactor) {
|
|
||||||
super(initialCapacity, loadFactor);
|
|
||||||
}
|
}
|
||||||
|
return new MultiKeyMap(map);
|
||||||
/**
|
|
||||||
* Constructor copying elements from another map.
|
|
||||||
*
|
|
||||||
* @param map the map to copy
|
|
||||||
* @throws NullPointerException if the map is null
|
|
||||||
*/
|
|
||||||
public MultiKeyMap(Map map) {
|
|
||||||
super(map);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Clones the map without cloning the keys or values.
|
* Constructs a new MultiKeyMap that decorates a <code>HashedMap</code>.
|
||||||
|
*/
|
||||||
|
public MultiKeyMap() {
|
||||||
|
super();
|
||||||
|
map = new HashedMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor that decorates the specified map.
|
||||||
|
* The map must not be null and should be empty or only contain valid keys.
|
||||||
|
* This constructor performs no validation.
|
||||||
*
|
*
|
||||||
* @return a shallow clone
|
* @param map the map to decorate
|
||||||
*/
|
*/
|
||||||
public Object clone() {
|
protected MultiKeyMap(AbstractHashedMap map) {
|
||||||
return super.clone();
|
super();
|
||||||
}
|
this.map = map;
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the map out using a custom routine.
|
|
||||||
*/
|
|
||||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
|
||||||
out.defaultWriteObject();
|
|
||||||
doWriteObject(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the map in using a custom routine.
|
|
||||||
*/
|
|
||||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
|
||||||
in.defaultReadObject();
|
|
||||||
doReadObject(in);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
@ -137,7 +129,7 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public Object get(Object key1, Object key2) {
|
public Object get(Object key1, Object key2) {
|
||||||
int hashCode = hash(key1, key2);
|
int hashCode = hash(key1, key2);
|
||||||
HashEntry entry = data[hashIndex(hashCode, data.length)];
|
AbstractHashedMap.HashEntry entry = map.data[map.hashIndex(hashCode, map.data.length)];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
|
||||||
return entry.getValue();
|
return entry.getValue();
|
||||||
|
@ -156,7 +148,7 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public boolean containsKey(Object key1, Object key2) {
|
public boolean containsKey(Object key1, Object key2) {
|
||||||
int hashCode = hash(key1, key2);
|
int hashCode = hash(key1, key2);
|
||||||
HashEntry entry = data[hashIndex(hashCode, data.length)];
|
AbstractHashedMap.HashEntry entry = map.data[map.hashIndex(hashCode, map.data.length)];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -176,18 +168,18 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public Object put(Object key1, Object key2, Object value) {
|
public Object put(Object key1, Object key2, Object value) {
|
||||||
int hashCode = hash(key1, key2);
|
int hashCode = hash(key1, key2);
|
||||||
int index = hashIndex(hashCode, data.length);
|
int index = map.hashIndex(hashCode, map.data.length);
|
||||||
HashEntry entry = data[index];
|
AbstractHashedMap.HashEntry entry = map.data[index];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
|
||||||
Object oldValue = entry.getValue();
|
Object oldValue = entry.getValue();
|
||||||
updateEntry(entry, value);
|
map.updateEntry(entry, value);
|
||||||
return oldValue;
|
return oldValue;
|
||||||
}
|
}
|
||||||
entry = entry.next;
|
entry = entry.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
addMapping(index, hashCode, new MultiKey(key1, key2), value);
|
map.addMapping(index, hashCode, new MultiKey(key1, key2), value);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,13 +192,13 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public Object remove(Object key1, Object key2) {
|
public Object remove(Object key1, Object key2) {
|
||||||
int hashCode = hash(key1, key2);
|
int hashCode = hash(key1, key2);
|
||||||
int index = hashIndex(hashCode, data.length);
|
int index = map.hashIndex(hashCode, map.data.length);
|
||||||
HashEntry entry = data[index];
|
AbstractHashedMap.HashEntry entry = map.data[index];
|
||||||
HashEntry previous = null;
|
AbstractHashedMap.HashEntry previous = null;
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
|
||||||
Object oldValue = entry.getValue();
|
Object oldValue = entry.getValue();
|
||||||
removeMapping(entry, index, previous);
|
map.removeMapping(entry, index, previous);
|
||||||
return oldValue;
|
return oldValue;
|
||||||
}
|
}
|
||||||
previous = entry;
|
previous = entry;
|
||||||
|
@ -245,7 +237,7 @@ public class MultiKeyMap
|
||||||
* @param key2 the second key
|
* @param key2 the second key
|
||||||
* @return true if the key matches
|
* @return true if the key matches
|
||||||
*/
|
*/
|
||||||
protected boolean isEqualKey(HashEntry entry, Object key1, Object key2) {
|
protected boolean isEqualKey(AbstractHashedMap.HashEntry entry, Object key1, Object key2) {
|
||||||
MultiKey multi = (MultiKey) entry.getKey();
|
MultiKey multi = (MultiKey) entry.getKey();
|
||||||
return
|
return
|
||||||
multi.size() == 2 &&
|
multi.size() == 2 &&
|
||||||
|
@ -264,7 +256,7 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public Object get(Object key1, Object key2, Object key3) {
|
public Object get(Object key1, Object key2, Object key3) {
|
||||||
int hashCode = hash(key1, key2, key3);
|
int hashCode = hash(key1, key2, key3);
|
||||||
HashEntry entry = data[hashIndex(hashCode, data.length)];
|
AbstractHashedMap.HashEntry entry = map.data[map.hashIndex(hashCode, map.data.length)];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
|
||||||
return entry.getValue();
|
return entry.getValue();
|
||||||
|
@ -284,7 +276,7 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public boolean containsKey(Object key1, Object key2, Object key3) {
|
public boolean containsKey(Object key1, Object key2, Object key3) {
|
||||||
int hashCode = hash(key1, key2, key3);
|
int hashCode = hash(key1, key2, key3);
|
||||||
HashEntry entry = data[hashIndex(hashCode, data.length)];
|
AbstractHashedMap.HashEntry entry = map.data[map.hashIndex(hashCode, map.data.length)];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -305,18 +297,18 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public Object put(Object key1, Object key2, Object key3, Object value) {
|
public Object put(Object key1, Object key2, Object key3, Object value) {
|
||||||
int hashCode = hash(key1, key2, key3);
|
int hashCode = hash(key1, key2, key3);
|
||||||
int index = hashIndex(hashCode, data.length);
|
int index = map.hashIndex(hashCode, map.data.length);
|
||||||
HashEntry entry = data[index];
|
AbstractHashedMap.HashEntry entry = map.data[index];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
|
||||||
Object oldValue = entry.getValue();
|
Object oldValue = entry.getValue();
|
||||||
updateEntry(entry, value);
|
map.updateEntry(entry, value);
|
||||||
return oldValue;
|
return oldValue;
|
||||||
}
|
}
|
||||||
entry = entry.next;
|
entry = entry.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
addMapping(index, hashCode, new MultiKey(key1, key2, key3), value);
|
map.addMapping(index, hashCode, new MultiKey(key1, key2, key3), value);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,13 +322,13 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public Object remove(Object key1, Object key2, Object key3) {
|
public Object remove(Object key1, Object key2, Object key3) {
|
||||||
int hashCode = hash(key1, key2, key3);
|
int hashCode = hash(key1, key2, key3);
|
||||||
int index = hashIndex(hashCode, data.length);
|
int index = map.hashIndex(hashCode, map.data.length);
|
||||||
HashEntry entry = data[index];
|
AbstractHashedMap.HashEntry entry = map.data[index];
|
||||||
HashEntry previous = null;
|
AbstractHashedMap.HashEntry previous = null;
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
|
||||||
Object oldValue = entry.getValue();
|
Object oldValue = entry.getValue();
|
||||||
removeMapping(entry, index, previous);
|
map.removeMapping(entry, index, previous);
|
||||||
return oldValue;
|
return oldValue;
|
||||||
}
|
}
|
||||||
previous = entry;
|
previous = entry;
|
||||||
|
@ -380,7 +372,7 @@ public class MultiKeyMap
|
||||||
* @param key3 the third key
|
* @param key3 the third key
|
||||||
* @return true if the key matches
|
* @return true if the key matches
|
||||||
*/
|
*/
|
||||||
protected boolean isEqualKey(HashEntry entry, Object key1, Object key2, Object key3) {
|
protected boolean isEqualKey(AbstractHashedMap.HashEntry entry, Object key1, Object key2, Object key3) {
|
||||||
MultiKey multi = (MultiKey) entry.getKey();
|
MultiKey multi = (MultiKey) entry.getKey();
|
||||||
return
|
return
|
||||||
multi.size() == 3 &&
|
multi.size() == 3 &&
|
||||||
|
@ -401,7 +393,7 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public Object get(Object key1, Object key2, Object key3, Object key4) {
|
public Object get(Object key1, Object key2, Object key3, Object key4) {
|
||||||
int hashCode = hash(key1, key2, key3, key4);
|
int hashCode = hash(key1, key2, key3, key4);
|
||||||
HashEntry entry = data[hashIndex(hashCode, data.length)];
|
AbstractHashedMap.HashEntry entry = map.data[map.hashIndex(hashCode, map.data.length)];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
|
||||||
return entry.getValue();
|
return entry.getValue();
|
||||||
|
@ -422,7 +414,7 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public boolean containsKey(Object key1, Object key2, Object key3, Object key4) {
|
public boolean containsKey(Object key1, Object key2, Object key3, Object key4) {
|
||||||
int hashCode = hash(key1, key2, key3, key4);
|
int hashCode = hash(key1, key2, key3, key4);
|
||||||
HashEntry entry = data[hashIndex(hashCode, data.length)];
|
AbstractHashedMap.HashEntry entry = map.data[map.hashIndex(hashCode, map.data.length)];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -444,18 +436,18 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public Object put(Object key1, Object key2, Object key3, Object key4, Object value) {
|
public Object put(Object key1, Object key2, Object key3, Object key4, Object value) {
|
||||||
int hashCode = hash(key1, key2, key3, key4);
|
int hashCode = hash(key1, key2, key3, key4);
|
||||||
int index = hashIndex(hashCode, data.length);
|
int index = map.hashIndex(hashCode, map.data.length);
|
||||||
HashEntry entry = data[index];
|
AbstractHashedMap.HashEntry entry = map.data[index];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
|
||||||
Object oldValue = entry.getValue();
|
Object oldValue = entry.getValue();
|
||||||
updateEntry(entry, value);
|
map.updateEntry(entry, value);
|
||||||
return oldValue;
|
return oldValue;
|
||||||
}
|
}
|
||||||
entry = entry.next;
|
entry = entry.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
addMapping(index, hashCode, new MultiKey(key1, key2, key3, key4), value);
|
map.addMapping(index, hashCode, new MultiKey(key1, key2, key3, key4), value);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,13 +462,13 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public Object remove(Object key1, Object key2, Object key3, Object key4) {
|
public Object remove(Object key1, Object key2, Object key3, Object key4) {
|
||||||
int hashCode = hash(key1, key2, key3, key4);
|
int hashCode = hash(key1, key2, key3, key4);
|
||||||
int index = hashIndex(hashCode, data.length);
|
int index = map.hashIndex(hashCode, map.data.length);
|
||||||
HashEntry entry = data[index];
|
AbstractHashedMap.HashEntry entry = map.data[index];
|
||||||
HashEntry previous = null;
|
AbstractHashedMap.HashEntry previous = null;
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
|
||||||
Object oldValue = entry.getValue();
|
Object oldValue = entry.getValue();
|
||||||
removeMapping(entry, index, previous);
|
map.removeMapping(entry, index, previous);
|
||||||
return oldValue;
|
return oldValue;
|
||||||
}
|
}
|
||||||
previous = entry;
|
previous = entry;
|
||||||
|
@ -525,7 +517,7 @@ public class MultiKeyMap
|
||||||
* @param key4 the fourth key
|
* @param key4 the fourth key
|
||||||
* @return true if the key matches
|
* @return true if the key matches
|
||||||
*/
|
*/
|
||||||
protected boolean isEqualKey(HashEntry entry, Object key1, Object key2, Object key3, Object key4) {
|
protected boolean isEqualKey(AbstractHashedMap.HashEntry entry, Object key1, Object key2, Object key3, Object key4) {
|
||||||
MultiKey multi = (MultiKey) entry.getKey();
|
MultiKey multi = (MultiKey) entry.getKey();
|
||||||
return
|
return
|
||||||
multi.size() == 4 &&
|
multi.size() == 4 &&
|
||||||
|
@ -548,7 +540,7 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public Object get(Object key1, Object key2, Object key3, Object key4, Object key5) {
|
public Object get(Object key1, Object key2, Object key3, Object key4, Object key5) {
|
||||||
int hashCode = hash(key1, key2, key3, key4, key5);
|
int hashCode = hash(key1, key2, key3, key4, key5);
|
||||||
HashEntry entry = data[hashIndex(hashCode, data.length)];
|
AbstractHashedMap.HashEntry entry = map.data[map.hashIndex(hashCode, map.data.length)];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
|
||||||
return entry.getValue();
|
return entry.getValue();
|
||||||
|
@ -570,7 +562,7 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public boolean containsKey(Object key1, Object key2, Object key3, Object key4, Object key5) {
|
public boolean containsKey(Object key1, Object key2, Object key3, Object key4, Object key5) {
|
||||||
int hashCode = hash(key1, key2, key3, key4, key5);
|
int hashCode = hash(key1, key2, key3, key4, key5);
|
||||||
HashEntry entry = data[hashIndex(hashCode, data.length)];
|
AbstractHashedMap.HashEntry entry = map.data[map.hashIndex(hashCode, map.data.length)];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -593,18 +585,18 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public Object put(Object key1, Object key2, Object key3, Object key4, Object key5, Object value) {
|
public Object put(Object key1, Object key2, Object key3, Object key4, Object key5, Object value) {
|
||||||
int hashCode = hash(key1, key2, key3, key4, key5);
|
int hashCode = hash(key1, key2, key3, key4, key5);
|
||||||
int index = hashIndex(hashCode, data.length);
|
int index = map.hashIndex(hashCode, map.data.length);
|
||||||
HashEntry entry = data[index];
|
AbstractHashedMap.HashEntry entry = map.data[index];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
|
||||||
Object oldValue = entry.getValue();
|
Object oldValue = entry.getValue();
|
||||||
updateEntry(entry, value);
|
map.updateEntry(entry, value);
|
||||||
return oldValue;
|
return oldValue;
|
||||||
}
|
}
|
||||||
entry = entry.next;
|
entry = entry.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
addMapping(index, hashCode, new MultiKey(key1, key2, key3, key4, key5), value);
|
map.addMapping(index, hashCode, new MultiKey(key1, key2, key3, key4, key5), value);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,13 +612,13 @@ public class MultiKeyMap
|
||||||
*/
|
*/
|
||||||
public Object remove(Object key1, Object key2, Object key3, Object key4, Object key5) {
|
public Object remove(Object key1, Object key2, Object key3, Object key4, Object key5) {
|
||||||
int hashCode = hash(key1, key2, key3, key4, key5);
|
int hashCode = hash(key1, key2, key3, key4, key5);
|
||||||
int index = hashIndex(hashCode, data.length);
|
int index = map.hashIndex(hashCode, map.data.length);
|
||||||
HashEntry entry = data[index];
|
AbstractHashedMap.HashEntry entry = map.data[index];
|
||||||
HashEntry previous = null;
|
AbstractHashedMap.HashEntry previous = null;
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
|
if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
|
||||||
Object oldValue = entry.getValue();
|
Object oldValue = entry.getValue();
|
||||||
removeMapping(entry, index, previous);
|
map.removeMapping(entry, index, previous);
|
||||||
return oldValue;
|
return oldValue;
|
||||||
}
|
}
|
||||||
previous = entry;
|
previous = entry;
|
||||||
|
@ -680,7 +672,7 @@ public class MultiKeyMap
|
||||||
* @param key5 the fifth key
|
* @param key5 the fifth key
|
||||||
* @return true if the key matches
|
* @return true if the key matches
|
||||||
*/
|
*/
|
||||||
protected boolean isEqualKey(HashEntry entry, Object key1, Object key2, Object key3, Object key4, Object key5) {
|
protected boolean isEqualKey(AbstractHashedMap.HashEntry entry, Object key1, Object key2, Object key3, Object key4, Object key5) {
|
||||||
MultiKey multi = (MultiKey) entry.getKey();
|
MultiKey multi = (MultiKey) entry.getKey();
|
||||||
return
|
return
|
||||||
multi.size() == 5 &&
|
multi.size() == 5 &&
|
||||||
|
@ -798,19 +790,119 @@ public class MultiKeyMap
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Override superclass to ensure that input keys are valid MultiKey objects.
|
* Check to ensure that input keys are valid MultiKey objects.
|
||||||
*
|
*
|
||||||
* @param key the key to check
|
* @param key the key to check
|
||||||
* @return the validated key
|
|
||||||
*/
|
*/
|
||||||
protected Object convertKey(Object key) {
|
protected void checkKey(Object key) {
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new NullPointerException("Key must not be null");
|
throw new NullPointerException("Key must not be null");
|
||||||
}
|
}
|
||||||
if (key instanceof MultiKey == false) {
|
if (key instanceof MultiKey == false) {
|
||||||
throw new ClassCastException("Key must be a MultiKey");
|
throw new ClassCastException("Key must be a MultiKey");
|
||||||
}
|
}
|
||||||
return key;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones the map without cloning the keys or values.
|
||||||
|
*
|
||||||
|
* @return a shallow clone
|
||||||
|
*/
|
||||||
|
public Object clone() {
|
||||||
|
return new MultiKeyMap((AbstractHashedMap) map.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts the key and value into the map, where the key must be a non-null
|
||||||
|
* MultiKey object.
|
||||||
|
*
|
||||||
|
* @param key the non-null MultiKey object
|
||||||
|
* @param value the value to store
|
||||||
|
* @return the previous value for the key
|
||||||
|
* @throws NullPointerException if the key is null
|
||||||
|
* @throws ClassCastException if the key is not a MultiKey
|
||||||
|
*/
|
||||||
|
public Object put(Object key, Object value) {
|
||||||
|
checkKey(key);
|
||||||
|
return map.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts all the keys and values into this map.
|
||||||
|
* Each key must be non-null and a MultiKey object.
|
||||||
|
*
|
||||||
|
* @param key the non-null MultiKey object
|
||||||
|
* @param value the value to store
|
||||||
|
* @return the previous value for the key
|
||||||
|
* @throws NullPointerException if the mapToCopy or any key within is null
|
||||||
|
* @throws ClassCastException if any key is not a MultiKey
|
||||||
|
*/
|
||||||
|
public void putAll(Map mapToCopy) {
|
||||||
|
for (Iterator it = mapToCopy.keySet().iterator(); it.hasNext();) {
|
||||||
|
Object key = it.next();
|
||||||
|
checkKey(key);
|
||||||
|
}
|
||||||
|
map.putAll(mapToCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
public MapIterator mapIterator() {
|
||||||
|
return map.mapIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return map.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return map.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
return map.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
return map.containsValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object get(Object key) {
|
||||||
|
return map.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object remove(Object key) {
|
||||||
|
return map.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set keySet() {
|
||||||
|
return map.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection values() {
|
||||||
|
return map.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set entrySet() {
|
||||||
|
return map.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return map.equals(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return map.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return map.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ import org.apache.commons.collections.keyvalue.MultiKey;
|
||||||
/**
|
/**
|
||||||
* JUnit tests.
|
* JUnit tests.
|
||||||
*
|
*
|
||||||
* @version $Revision: 1.1 $ $Date: 2004/04/12 12:05:30 $
|
* @version $Revision: 1.2 $ $Date: 2004/04/30 23:51:36 $
|
||||||
*
|
*
|
||||||
* @author Stephen Colebourne
|
* @author Stephen Colebourne
|
||||||
*/
|
*/
|
||||||
|
@ -110,6 +110,27 @@ public class TestMultiKeyMap extends AbstractTestIterableMap {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
public void testNullHandling() {
|
||||||
|
resetFull();
|
||||||
|
assertEquals(null, map.get(null));
|
||||||
|
assertEquals(false, map.containsKey(null));
|
||||||
|
assertEquals(false, map.containsValue(null));
|
||||||
|
assertEquals(null, map.remove(null));
|
||||||
|
assertEquals(false, map.entrySet().contains(null));
|
||||||
|
assertEquals(false, map.keySet().contains(null));
|
||||||
|
assertEquals(false, map.values().contains(null));
|
||||||
|
try {
|
||||||
|
map.put(null, null);
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException ex) {}
|
||||||
|
assertEquals(null, map.put(new MultiKey(null, null), null));
|
||||||
|
try {
|
||||||
|
map.put(null, new Object());
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException ex) {}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
public void testMultiKeyGet() {
|
public void testMultiKeyGet() {
|
||||||
resetFull();
|
resetFull();
|
||||||
|
@ -390,6 +411,30 @@ public class TestMultiKeyMap extends AbstractTestIterableMap {
|
||||||
assertSame(map.get(new MultiKey(I1, I2)), cloned.get(new MultiKey(I1, I2)));
|
assertSame(map.get(new MultiKey(I1, I2)), cloned.get(new MultiKey(I1, I2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
public void testLRUMultiKeyMap() {
|
||||||
|
MultiKeyMap map = MultiKeyMap.decorate(new LRUMap(2));
|
||||||
|
map.put(I1, I2, "1-2");
|
||||||
|
map.put(I1, I3, "1-3");
|
||||||
|
assertEquals(2, map.size());
|
||||||
|
map.put(I1, I4, "1-4");
|
||||||
|
assertEquals(2, map.size());
|
||||||
|
assertEquals(true, map.containsKey(I1, I3));
|
||||||
|
assertEquals(true, map.containsKey(I1, I4));
|
||||||
|
assertEquals(false, map.containsKey(I1, I2));
|
||||||
|
|
||||||
|
MultiKeyMap cloned = (MultiKeyMap) map.clone();
|
||||||
|
assertEquals(2, map.size());
|
||||||
|
assertEquals(true, cloned.containsKey(I1, I3));
|
||||||
|
assertEquals(true, cloned.containsKey(I1, I4));
|
||||||
|
assertEquals(false, cloned.containsKey(I1, I2));
|
||||||
|
cloned.put(I1, I5, "1-5");
|
||||||
|
assertEquals(2, cloned.size());
|
||||||
|
assertEquals(true, cloned.containsKey(I1, I4));
|
||||||
|
assertEquals(true, cloned.containsKey(I1, I5));
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
public String getCompatibilityVersion() {
|
public String getCompatibilityVersion() {
|
||||||
return "3.1";
|
return "3.1";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue