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

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

package org.codehaus.plexus.util;

/*
 * 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).
 */

import java.util.Collection;
import java.util.Collections;
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 maintain 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 (Object key : backingMap.keySet()) { _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 (Object key : _backingMap.keySet()) { _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. */ @Override 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. */ @Override 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. */ @Override 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. */ @Override 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. */ @Override 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. */ @Override 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. */ @Override 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. */ @Override 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. */ @Override 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. */ @Override 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. */ @Override 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. */ @Override 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. */ @Override 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. */ @Override public int hashCode() { return _backingMap.hashCode(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy