All Downloads are FREE. Search and download functionalities are using the official Maven repository.

collections-generic-4.01.src.java.org.apache.commons.collections15.map.MultiKeyMap Maven / Gradle / Ivy

// GenericsNote: Converted.
/*
 *  Copyright 2004 The Apache Software Foundation
 *
 *  Licensed 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.collections15.map;

import org.apache.commons.collections15.IterableMap;
import org.apache.commons.collections15.MapIterator;
import org.apache.commons.collections15.keyvalue.MultiKey;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * A Map implementation that uses multiple keys to map the value.
 * 

* This class is the most efficient way to uses multiple keys to map to a value. * The best way to use this class is via the additional map-style methods. * These provide get, containsKey, put and * remove for individual keys which operate without extra object creation. *

* The additional methods are the main interface of this map. * As such, you will not normally hold this map in a variable of type Map. *

* The normal map methods take in and return a {@link MultiKey}. * If you try to use put() with any other object type a * ClassCastException is thrown. If you try to use null as * the key in put() a NullPointerException is thrown. *

* This map is implemented as a decorator of a AbstractHashedMap which * enables extra behaviour to be added easily. *

    *
  • MultiKeyMap.decorate(new LinkedMap()) creates an ordered map. *
  • MultiKeyMap.decorate(new LRUMap()) creates an least recently used map. *
  • MultiKeyMap.decorate(new ReferenceMap()) creates a garbage collector sensitive map. *
* Note that IdentityMap and ReferenceIdentityMap are unsuitable * for use as the key comparison would work on the whole MultiKey, not the elements within. *

* As an example, consider a least recently used cache that uses a String airline code * and a Locale to lookup the airline's name: *

 * private MultiKeyMap cache = MultiKeyMap.decorate(new LRUMap(50));
 * 

* public String getAirlineName(String code, String locale) { * String name = (String) cache.get(code, locale); * if (name == null) { * name = getAirlineNameFromDB(code, locale); * cache.put(code, locale, name); * } * return name; * } *

* * @author Matt Hall, John Watkinson, Stephen Colebourne * @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:32 $ * @since Commons Collections 3.1 */ public class MultiKeyMap implements IterableMap, V>, Serializable { /** * Serialisation version */ private static final long serialVersionUID = -1788199231038721040L; /** * The decorated map */ protected final AbstractHashedMap, V> map; //----------------------------------------------------------------------- /** * Decorates the specified map to add the MultiKeyMap API and fast query. * The map must not be null and must be empty. * * @param map the map to decorate, not null * @throws IllegalArgumentException if the map is null or not empty */ public static MultiKeyMap decorate(AbstractHashedMap, V> map) { if (map == null) { throw new IllegalArgumentException("Map must not be null"); } if (map.size() > 0) { throw new IllegalArgumentException("Map must be empty"); } return new MultiKeyMap(map); } //----------------------------------------------------------------------- /** * Constructs a new MultiKeyMap that decorates a HashedMap. */ public MultiKeyMap() { super(); map = new HashedMap, V>(); } /** * Constructor that decorates the specified map and is called from * {@link #decorate(AbstractHashedMap)}. * The map must not be null and should be empty or only contain valid keys. * This constructor performs no validation. * * @param map the map to decorate */ protected MultiKeyMap(AbstractHashedMap, V> map) { super(); this.map = map; } //----------------------------------------------------------------------- /** * Gets the value mapped to the specified multi-key. * * @param keys the keys * @return the mapped value, null if no match */ public V get(K... keys) { int hashCode = hash(keys); AbstractHashedMap.HashEntry, V> entry = map.data[map.hashIndex(hashCode, map.data.length)]; while (entry != null) { if (entry.hashCode == hashCode && isEqualKey(entry, keys)) { return entry.getValue(); } entry = entry.next; } return null; } /** * Checks whether the map contains the specified multi-key. * * @param keys the keys * @return true if the map contains the key */ public boolean containsKey(K... keys) { int hashCode = hash(keys); AbstractHashedMap.HashEntry, V> entry = map.data[map.hashIndex(hashCode, map.data.length)]; while (entry != null) { if (entry.hashCode == hashCode && isEqualKey(entry, keys)) { return true; } entry = entry.next; } return false; } /** * For backwards compatibility, makes a call to the new varargs {@link MultiKeyMap#putMultiKey} */ public V put(K key1, K key2, V value) { return putMultiKey(value, key1, key2); } /** * For backwards compatibility, makes a call to the new varargs {@link MultiKeyMap#putMultiKey} */ public V put(K key1, K key2, K key3, V value) { return putMultiKey(value, key1, key2, key3); } /** * For backwards compatibility, makes a call to the new varargs {@link MultiKeyMap#putMultiKey} */ public V put(K key1, K key2, K key3, K key4, V value) { return putMultiKey(value, key1, key2, key3, key4); } /** * For backwards compatibility, makes a call to the new varargs {@link MultiKeyMap#putMultiKey} */ public V put(K key1, K key2, K key3, K key4, K key5, V value) { return putMultiKey(value, key1, key2, key3, key4, key5); } /** * Stores the value against the specified multi-key. * * @param value the value to store * @param keys the keys * @return the value previously mapped to this combined key, null if none */ public V putMultiKey(V value, K... keys) { int hashCode = hash(keys); int index = map.hashIndex(hashCode, map.data.length); AbstractHashedMap.HashEntry, V> entry = map.data[index]; while (entry != null) { if (entry.hashCode == hashCode && isEqualKey(entry, keys)) { V oldValue = entry.getValue(); map.updateEntry(entry, value); return oldValue; } entry = entry.next; } map.addMapping(index, hashCode, new MultiKey(keys), value); return null; } /** * Removes the specified multi-key from this map. * * @param keys the keys * @return the value mapped to the removed key, null if key not in map */ public Object remove(K... keys) { int hashCode = hash(keys); int index = map.hashIndex(hashCode, map.data.length); AbstractHashedMap.HashEntry, V> entry = map.data[index]; AbstractHashedMap.HashEntry, V> previous = null; while (entry != null) { if (entry.hashCode == hashCode && isEqualKey(entry, keys)) { Object oldValue = entry.getValue(); map.removeMapping(entry, index, previous); return oldValue; } previous = entry; entry = entry.next; } return null; } /** * Gets the hash code for the specified multi-key. * * @param keys the keys * @return the hash code */ protected int hash(K... keys) { int h = 0; for (int i = 0; i < keys.length; i++) { K key = keys[i]; if (key != null) { h ^= key.hashCode(); } } h += ~(h << 9); h ^= (h >>> 14); h += (h << 4); h ^= (h >>> 10); return h; } /** * Is the key equal to the combined key. * * @param entry the entry to compare to * @param keys the keys * @return true if the key matches */ protected boolean isEqualKey(AbstractHashedMap.HashEntry, V> entry, K... keys) { MultiKey multi = (MultiKey) entry.getKey(); if (multi.size() != keys.length) { return false; } else { for (int i = 0; i < keys.length; i++) { K key = keys[i]; if ((key == null ? multi.getKey(i) != null : !key.equals(multi.getKey(i)))) { return false; } } } return true; } //----------------------------------------------------------------------- /** * Removes all mappings where the first key is that specified. *

* This method removes all the mappings where the MultiKey * has one or more keys, and the first matches that specified. * * @param key1 the first key * @return true if any elements were removed */ public boolean removeAll(Object key1) { boolean modified = false; MapIterator it = mapIterator(); while (it.hasNext()) { MultiKey multi = (MultiKey) it.next(); if (multi.size() >= 1 && (key1 == null ? multi.getKey(0) == null : key1.equals(multi.getKey(0)))) { it.remove(); modified = true; } } return modified; } /** * Removes all mappings where the first two keys are those specified. *

* This method removes all the mappings where the MultiKey * has two or more keys, and the first two match those specified. * * @param key1 the first key * @param key2 the second key * @return true if any elements were removed */ public boolean removeAll(Object key1, Object key2) { boolean modified = false; MapIterator it = mapIterator(); while (it.hasNext()) { MultiKey multi = (MultiKey) it.next(); if (multi.size() >= 2 && (key1 == null ? multi.getKey(0) == null : key1.equals(multi.getKey(0))) && (key2 == null ? multi.getKey(1) == null : key2.equals(multi.getKey(1)))) { it.remove(); modified = true; } } return modified; } /** * Removes all mappings where the first three keys are those specified. *

* This method removes all the mappings where the MultiKey * has three or more keys, and the first three match those specified. * * @param key1 the first key * @param key2 the second key * @param key3 the third key * @return true if any elements were removed */ public boolean removeAll(Object key1, Object key2, Object key3) { boolean modified = false; MapIterator it = mapIterator(); while (it.hasNext()) { MultiKey multi = (MultiKey) it.next(); if (multi.size() >= 3 && (key1 == null ? multi.getKey(0) == null : key1.equals(multi.getKey(0))) && (key2 == null ? multi.getKey(1) == null : key2.equals(multi.getKey(1))) && (key3 == null ? multi.getKey(2) == null : key3.equals(multi.getKey(2)))) { it.remove(); modified = true; } } return modified; } /** * Removes all mappings where the first four keys are those specified. *

* This method removes all the mappings where the MultiKey * has four or more keys, and the first four match those specified. * * @param key1 the first key * @param key2 the second key * @param key3 the third key * @param key4 the fourth key * @return true if any elements were removed */ public boolean removeAll(Object key1, Object key2, Object key3, Object key4) { boolean modified = false; MapIterator it = mapIterator(); while (it.hasNext()) { MultiKey multi = (MultiKey) it.next(); if (multi.size() >= 4 && (key1 == null ? multi.getKey(0) == null : key1.equals(multi.getKey(0))) && (key2 == null ? multi.getKey(1) == null : key2.equals(multi.getKey(1))) && (key3 == null ? multi.getKey(2) == null : key3.equals(multi.getKey(2))) && (key4 == null ? multi.getKey(3) == null : key4.equals(multi.getKey(3)))) { it.remove(); modified = true; } } return modified; } //----------------------------------------------------------------------- /** * Check to ensure that input keys are valid MultiKey objects. * * @param key the key to check */ protected void checkKey(Object key) { if (key == null) { throw new NullPointerException("Key must not be null"); } } /** * 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 V put(MultiKey key, V 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 mapToCopy the map to copy in. * @throws NullPointerException if the mapToCopy or any key within is null * @throws ClassCastException if any key is not a MultiKey */ public void putAll(Map, ? extends V> 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 V get(Object key) { return map.get(key); } public V 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(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy