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

org.codehaus.plexus.util.CachedMap Maven / Gradle / Ivy

The newest version!
/*
 * J.A.D.E. Java(TM) Addition to Default Environment.
 * Latest release available at http://jade.dautelle.com/
 * This class is public domain (not copyrighted).
 */
package org.codehaus.plexus.util;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 

This class provides cache access to Map collections.

* *

Instance of this class can be used as "proxy" for any collection * implementing the java.util.Map interface.

* *

Typically, {@link CachedMap} are used to accelerate access to * large collections when the access to the collection is not evenly * distributed (associative cache). The performance gain is about * 50% for the fastest hash map collections (e.g. {@link FastMap}). * For slower collections such as java.util.TreeMap, * non-resizable {@link FastMap} (real-time) or database access, * performance can be of several orders of magnitude.

* *

Note: The keys used to access elements of a {@link CachedMap} do * not need to be immutable as they are not stored in the cache * (only keys specified by the {@link #put} method are). * In other words, access can be performed using mutable keys as long * as these keys can be compared for equality with the real map's keys * (e.g. same hashCode values).

* *

This implementation is not synchronized. Multiple threads accessing * or modifying the collection must be synchronized externally.

* *

This class is public domain (not copyrighted).

* * @author Jean-Marie Dautelle * @version 5.3, October 30, 2003 */ public final class CachedMap implements Map { /** * Holds the FastMap backing this collection * (null if generic backing map). */ private final FastMap _backingFastMap; /** * Holds the generic map backing this collection. */ private final Map _backingMap; /** * Holds the keys of the backing map (key-to-key mapping). * (null if FastMap backing map). */ private final FastMap _keysMap; /** * Holds the cache's mask (capacity - 1). */ private final int _mask; /** * Holds the keys being cached. */ private final Object[] _keys; /** * Holds the values being cached. */ private final Object[] _values; /** * Creates a cached map backed by a {@link FastMap}. * The default cache size and map capacity is set to 256 * entries. */ public CachedMap() { this(256, new FastMap()); } /** * Creates a cached map backed by a {@link FastMap} and having the * specified cache size. * * @param cacheSize the cache size, the actual cache size is the * first power of 2 greater or equal to cacheSize. * This is also the initial capacity of the backing map. */ public CachedMap(int cacheSize) { this(cacheSize, new FastMap(cacheSize)); } /** * Creates a cached map backed by the specified map and having the specified * cache size. In order to maitain cache veracity, it is critical * that all update to the backing map is accomplished through the * {@link CachedMap} instance; otherwise {@link #flush} has to be called. * * @param cacheSize the cache size, the actual cache size is the * first power of 2 greater or equal to cacheSize. * @param backingMap the backing map to be "wrapped" in a cached map. */ public CachedMap(int cacheSize, Map backingMap) { // Find a power of 2 >= minimalCache int actualCacheSize = 1; while (actualCacheSize < cacheSize) { actualCacheSize <<= 1; } // Sets up cache. _keys = new Object[actualCacheSize]; _values = new Object[actualCacheSize]; _mask = actualCacheSize - 1; // Sets backing map references. if (backingMap instanceof FastMap) { _backingFastMap = (FastMap)backingMap; _backingMap = _backingFastMap; _keysMap = null; } else { _backingFastMap = null; _backingMap = backingMap; _keysMap = new FastMap(backingMap.size()); for (Iterator i= backingMap.keySet().iterator(); i.hasNext();) { Object key = i.next(); _keysMap.put(key, key); } } } /** * Returns the actual cache size. * * @return the cache size (power of 2). */ public int getCacheSize() { return _keys.length; } /** * Returns the backing map. If the backing map is modified directly, * this {@link CachedMap} has to be flushed. * * @return the backing map. * @see #flush */ public Map getBackingMap() { return (_backingFastMap != null) ? _backingFastMap : _backingMap; } /** * Flushes the key/value pairs being cached. This method should be called * if the backing map is externally modified. */ public void flush() { for (int i=0; i < _keys.length; i++) { _keys[i] = null; _values[i] = null; } if (_keysMap != null) { // Re-populates keys from backing map. for (Iterator i= _backingMap.keySet().iterator(); i.hasNext();) { Object key = i.next(); _keysMap.put(key, key); } } } /** * Returns the value to which this map maps the specified key. * First, the cache is being checked, then if the cache does not contains * the specified key, the backing map is accessed and the key/value * pair is stored in the cache. * * @param key the key whose associated value is to be returned. * @return the value to which this map maps the specified key, or * null if the map contains no mapping for this key. * @throws ClassCastException if the key is of an inappropriate type for * the backing map (optional). * @throws NullPointerException if the key is null. */ public Object get(Object key) { int index = key.hashCode() & _mask; return key.equals(_keys[index]) ? _values[index] : getCacheMissed(key, index); } private Object getCacheMissed(Object key, int index) { if (_backingFastMap != null) { Map.Entry entry = _backingFastMap.getEntry(key); if (entry != null) { _keys[index] = entry.getKey(); Object value = entry.getValue(); _values[index] = value; return value; } else { return null; } } else { // Generic backing map. Object mapKey = _keysMap.get(key); if (mapKey != null) { _keys[index] = mapKey; Object value = _backingMap.get(key); _values[index] = value; return value; } else { return null; } } } /** * Associates the specified value with the specified key in this map. * * @param key the key with which the specified value is to be associated. * @param value the value to be associated with the specified key. * @return the previous value associated with specified key, or * null if there was no mapping for the key. * @throws UnsupportedOperationException if the put operation * is not supported by the backing map. * @throws ClassCastException if the class of the specified key or value * prevents it from being stored in this map. * @throws IllegalArgumentException if some aspect of this key or value * prevents it from being stored in this map. * @throws NullPointerException if the key is null. */ public Object put(Object key, Object value) { // Updates the cache. int index = key.hashCode() & _mask; if (key.equals(_keys[index]) ) { _values[index] = value; } else if (_keysMap != null) { // Possibly a new key. _keysMap.put(key, key); } // Updates the backing map. return _backingMap.put(key, value); } /** * Removes the mapping for this key from this map if it is present. * * @param key key whose mapping is to be removed from the map. * @return previous value associated with specified key, * or null if there was no mapping for key. * @throws ClassCastException if the key is of an inappropriate type for * the backing map (optional). * @throws NullPointerException if the key is null. * @throws UnsupportedOperationException if the remove method * is not supported by the backing map. */ public Object remove(Object key) { // Removes from cache. int index = key.hashCode() & _mask; if (key.equals(_keys[index]) ) { _keys[index] = null; } // Removes from key map. if (_keysMap != null) { _keysMap.remove(key); } // Removes from backing map. return _backingMap.remove(key); } /** * Indicates if this map contains a mapping for the specified key. * * @param key the key whose presence in this map is to be tested. * @return true if this map contains a mapping for the * specified key; false otherwise. */ public boolean containsKey(Object key) { // Checks the cache. int index = key.hashCode() & _mask; if (key.equals(_keys[index]) ) { return true; } else { // Checks the backing map. return _backingMap.containsKey(key); } } /** * Returns the number of key-value mappings in this map. If the * map contains more than Integer.MAX_VALUE elements, * returns Integer.MAX_VALUE. * * @return the number of key-value mappings in this map. */ public int size() { return _backingMap.size(); } /** * Returns true if this map contains no key-value mappings. * * @return true if this map contains no key-value mappings. */ public boolean isEmpty() { return _backingMap.isEmpty(); } /** * Returns true if this map maps one or more keys to the * specified value. * * @param value value whose presence in this map is to be tested. * @return true if this map maps one or more keys to the * specified value. * @throws ClassCastException if the value is of an inappropriate type for * the backing map (optional). * @throws NullPointerException if the value is null and the * backing map does not not permit null values. */ public boolean containsValue(Object value) { return _backingMap.containsValue(value); } /** * Copies all of the mappings from the specified map to this map * (optional operation). This method automatically flushes the cache. * * @param map the mappings to be stored in this map. * @throws UnsupportedOperationException if the putAll method * is not supported by the backing map. * @throws ClassCastException if the class of a key or value in the * specified map prevents it from being stored in this map. * @throws IllegalArgumentException some aspect of a key or value in the * specified map prevents it from being stored in this map. * @throws NullPointerException the specified map is null, or * if the backing map does not permit null keys or * values, and the specified map contains null keys or * values. */ public void putAll(Map map) { _backingMap.putAll(map); flush(); } /** * Removes all mappings from this map (optional operation). This method * automatically flushes the cache. * * @throws UnsupportedOperationException if clear is not supported by the * backing map. */ public void clear() { _backingMap.clear(); flush(); } /** * Returns an unmodifiable view of the keys contained in this * map. * * @return an unmodifiable view of the keys contained in this map. */ public Set keySet() { return Collections.unmodifiableSet(_backingMap.keySet()); } /** * Returns an unmodifiable view of the values contained in this map. * * @return an unmodifiable view of the values contained in this map. */ public Collection values() { return Collections.unmodifiableCollection(_backingMap.values()); } /** * Returns an unmodifiable view of the mappings contained in this * map. Each element in the returned set is a Map.Entry. * * @return an unmodifiable view of the mappings contained in this map. */ public Set entrySet() { return Collections.unmodifiableSet(_backingMap.entrySet()); } /** * Compares the specified object with this map for equality. Returns * true if the given object is also a map and the two Maps * represent the same mappings. * * @param o object to be compared for equality with this map. * @return true if the specified object is equal to this map. */ public boolean equals(Object o) { return _backingMap.equals(o); } /** * Returns the hash code value for this map. * * @return the hash code value for this map. */ public int hashCode() { return _backingMap.hashCode(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy