📄 referencemap.java
字号:
/** * Copyright (C) 2006 Google Inc. * * 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 com.opensymphony.xwork2.inject.util;import static com.opensymphony.xwork2.inject.util.ReferenceType.STRONG;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import java.lang.ref.Reference;import java.util.ArrayList;import java.util.Collection;import java.util.Collections;import java.util.HashSet;import java.util.Map;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentMap;/** * Concurrent hash map that wraps keys and/or values in soft or weak * references. Does not support null keys or values. Uses identity equality * for weak and soft keys. * * <p>The concurrent semantics of {@link ConcurrentHashMap} combined with the * fact that the garbage collector can asynchronously reclaim and clean up * after keys and values at any time can lead to some racy semantics. For * example, {@link #size()} returns an upper bound on the size, i.e. the actual * size may be smaller in cases where the key or value has been reclaimed but * the map entry has not been cleaned up yet. * * <p>Another example: If {@link #get(Object)} cannot find an existing entry * for a key, it will try to create one. This operation is not atomic. One * thread could {@link #put(Object, Object)} a value between the time another * thread running {@code get()} checks for an entry and decides to create one. * In this case, the newly created value will replace the put value in the * map. Also, two threads running {@code get()} concurrently can potentially * create duplicate values for a given key. * * <p>In other words, this class is great for caching but not atomicity. * * @author crazybob@google.com (Bob Lee) */@SuppressWarnings("unchecked")public class ReferenceMap<K, V> implements Map<K, V>, Serializable { private static final long serialVersionUID = 0; transient ConcurrentMap<Object, Object> delegate; final ReferenceType keyReferenceType; final ReferenceType valueReferenceType; /** * Concurrent hash map that wraps keys and/or values based on specified * reference types. * * @param keyReferenceType key reference type * @param valueReferenceType value reference type */ public ReferenceMap(ReferenceType keyReferenceType, ReferenceType valueReferenceType) { ensureNotNull(keyReferenceType, valueReferenceType); if (keyReferenceType == ReferenceType.PHANTOM || valueReferenceType == ReferenceType.PHANTOM) { throw new IllegalArgumentException("Phantom references not supported."); } this.delegate = new ConcurrentHashMap<Object, Object>(); this.keyReferenceType = keyReferenceType; this.valueReferenceType = valueReferenceType; } V internalGet(K key) { Object valueReference = delegate.get(makeKeyReferenceAware(key)); return valueReference == null ? null : (V) dereferenceValue(valueReference); } public V get(final Object key) { ensureNotNull(key); return internalGet((K) key); } V execute(Strategy strategy, K key, V value) { ensureNotNull(key, value); Object keyReference = referenceKey(key); Object valueReference = strategy.execute( this, keyReference, referenceValue(keyReference, value) ); return valueReference == null ? null : (V) dereferenceValue(valueReference); } public V put(K key, V value) { return execute(putStrategy(), key, value); } public V remove(Object key) { ensureNotNull(key); Object referenceAwareKey = makeKeyReferenceAware(key); Object valueReference = delegate.remove(referenceAwareKey); return valueReference == null ? null : (V) dereferenceValue(valueReference); } public int size() { return delegate.size(); } public boolean isEmpty() { return delegate.isEmpty(); } public boolean containsKey(Object key) { ensureNotNull(key); Object referenceAwareKey = makeKeyReferenceAware(key); return delegate.containsKey(referenceAwareKey); } public boolean containsValue(Object value) { ensureNotNull(value); for (Object valueReference : delegate.values()) { if (value.equals(dereferenceValue(valueReference))) { return true; } } return false; } public void putAll(Map<? extends K, ? extends V> t) { for (Map.Entry<? extends K, ? extends V> entry : t.entrySet()) { put(entry.getKey(), entry.getValue()); } } public void clear() { delegate.clear(); } /** * Returns an unmodifiable set view of the keys in this map. As this method * creates a defensive copy, the performance is O(n). */ public Set<K> keySet() { return Collections.unmodifiableSet( dereferenceKeySet(delegate.keySet())); } /** * Returns an unmodifiable set view of the values in this map. As this * method creates a defensive copy, the performance is O(n). */ public Collection<V> values() { return Collections.unmodifiableCollection( dereferenceValues(delegate.values())); } public V putIfAbsent(K key, V value) { // TODO (crazybob) if the value has been gc'ed but the entry hasn't been // cleaned up yet, this put will fail. return execute(putIfAbsentStrategy(), key, value); } public boolean remove(Object key, Object value) { ensureNotNull(key, value); Object referenceAwareKey = makeKeyReferenceAware(key); Object referenceAwareValue = makeValueReferenceAware(value); return delegate.remove(referenceAwareKey, referenceAwareValue); } public boolean replace(K key, V oldValue, V newValue) { ensureNotNull(key, oldValue, newValue); Object keyReference = referenceKey(key); Object referenceAwareOldValue = makeValueReferenceAware(oldValue); return delegate.replace( keyReference, referenceAwareOldValue, referenceValue(keyReference, newValue) ); } public V replace(K key, V value) { // TODO (crazybob) if the value has been gc'ed but the entry hasn't been // cleaned up yet, this will succeed when it probably shouldn't. return execute(replaceStrategy(), key, value); } /** * Returns an unmodifiable set view of the entries in this map. As this * method creates a defensive copy, the performance is O(n). */ public Set<Map.Entry<K, V>> entrySet() { Set<Map.Entry<K, V>> entrySet = new HashSet<Map.Entry<K, V>>(); for (Map.Entry<Object, Object> entry : delegate.entrySet()) { Map.Entry<K, V> dereferenced = dereferenceEntry(entry); if (dereferenced != null) { entrySet.add(dereferenced); } } return Collections.unmodifiableSet(entrySet); } /** * Dereferences an entry. Returns null if the key or value has been gc'ed. */ Entry dereferenceEntry(Map.Entry<Object, Object> entry) { K key = dereferenceKey(entry.getKey()); V value = dereferenceValue(entry.getValue()); return (key == null || value == null) ? null : new Entry(key, value); } /** * Creates a reference for a key. */ Object referenceKey(K key) { switch (keyReferenceType) { case STRONG: return key; case SOFT: return new SoftKeyReference(key); case WEAK: return new WeakKeyReference(key); default: throw new AssertionError(); } } /** * Converts a reference to a key. */ K dereferenceKey(Object o) { return (K) dereference(keyReferenceType, o); } /** * Converts a reference to a value. */ V dereferenceValue(Object o) { return (V) dereference(valueReferenceType, o); } /** * Returns the refererent for reference given its reference type. */ Object dereference(ReferenceType referenceType, Object reference) { return referenceType == STRONG ? reference : ((Reference) reference).get(); } /** * Creates a reference for a value. */ Object referenceValue(Object keyReference, Object value) { switch (valueReferenceType) { case STRONG: return value; case SOFT: return new SoftValueReference(keyReference, value); case WEAK: return new WeakValueReference(keyReference, value); default: throw new AssertionError(); } } /** * Dereferences a set of key references. */ Set<K> dereferenceKeySet(Set keyReferences) { return keyReferenceType == STRONG ? keyReferences : dereferenceCollection(keyReferenceType, keyReferences, new HashSet()); } /** * Dereferences a collection of value references. */ Collection<V> dereferenceValues(Collection valueReferences) { return valueReferenceType == STRONG ? valueReferences : dereferenceCollection(valueReferenceType, valueReferences, new ArrayList(valueReferences.size())); } /** * Wraps key so it can be compared to a referenced key for equality. */ Object makeKeyReferenceAware(Object o) { return keyReferenceType == STRONG ? o : new KeyReferenceAwareWrapper(o); } /** * Wraps value so it can be compared to a referenced value for equality. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -