Java tutorial
/* * Copyright 2002-2005 the original author or authors. * * 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.springframework.util; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * A simple decorator for a Map, encapsulating the workflow for caching * expensive values in a target Map. Supports caching weak or strong keys. * * <p>This class is also an abstract template. Caching Map implementations * should subclass and override the <code>create(key)</code> method which * encapsulates expensive creation of a new object. * * @author Keith Donald * @since 1.2.2 */ public abstract class CachingMapDecorator implements Map, Serializable { private static Object NULL_VALUE = new Object(); private static final Log logger = LogFactory.getLog(CachingMapDecorator.class); private final Map targetMap; /** * Create a CachingMapDecorator with strong keys, * using an underlying synchronized Map. */ public CachingMapDecorator() { this(false); } /** * Create a CachingMapDecorator, * using an underlying synchronized Map. * @param weakKeys whether to use weak references for keys */ public CachingMapDecorator(boolean weakKeys) { Map internalMap = weakKeys ? (Map) new WeakHashMap() : new HashMap(); this.targetMap = Collections.synchronizedMap(internalMap); } /** * Create a CachingMapDecorator with initial size, * using an underlying synchronized Map. * @param weakKeys whether to use weak references for keys * @param size the initial cache size */ public CachingMapDecorator(boolean weakKeys, int size) { Map internalMap = weakKeys ? (Map) new WeakHashMap(size) : new HashMap(size); this.targetMap = Collections.synchronizedMap(internalMap); } /** * Create a CachingMapDecorator for the given Map. * <p>The passed-in Map won't get synchronized explicitly, * so make sure to pass in a properly synchronized Map, if desired. * @param targetMap the Map to decorate */ public CachingMapDecorator(Map targetMap) { Assert.notNull(targetMap, "Target Map is required"); this.targetMap = targetMap; } public int size() { return this.targetMap.size(); } public boolean isEmpty() { return this.targetMap.isEmpty(); } public boolean containsKey(Object key) { return this.targetMap.containsKey(key); } public boolean containsValue(Object value) { return this.targetMap.containsValue(value); } public Object put(Object key, Object value) { return this.targetMap.put(key, value); } public Object remove(Object key) { return this.targetMap.remove(key); } public void putAll(Map t) { this.targetMap.putAll(t); } public void clear() { this.targetMap.clear(); } public Set keySet() { return this.targetMap.keySet(); } public Collection values() { return this.targetMap.values(); } public Set entrySet() { return this.targetMap.entrySet(); } /** * Get value for key. * Creates and caches value if it doesn't already exist in the cache. * <p>This implementation is <i>not</i> synchronized: This is highly * concurrent but does not guarantee unique instances in the cache, * as multiple values for the same key could get created in parallel. * Consider overriding this method to synchronize it, if desired. * @see #create(Object) */ public Object get(Object key) { Object value = this.targetMap.get(key); if (value == null) { if (logger.isDebugEnabled()) { logger.debug("Creating new expensive value for key '" + key + "'"); } value = create(key); if (value == null) { value = NULL_VALUE; } if (logger.isDebugEnabled()) { logger.debug("Caching expensive value: " + value); } put(key, value); } else { if (logger.isDebugEnabled()) { logger.debug("For key '" + key + "', returning cached value: " + value); } } return (value == NULL_VALUE) ? null : value; } /** * Create a value to cache for the given key. * Called by <code>get</code> if there is no value cached already. * @param key the cache key * @see #get(Object) */ protected abstract Object create(Object key); public String toString() { return "CachingMapDecorator [" + getClass().getName() + "]:" + this.targetMap; } }