/* Copyright (c) 2011 Danish Maritime Authority.
*
* 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.
*/
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package net.maritimecloud.internal.util.concurrent;
import java.io.IOException;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.ReentrantLock;
import sun.misc.Unsafe;
/**
* A {@link java.util.concurrent.ConcurrentMap} supporting user-defined equivalence comparisons, soft, weak, or strong
* keys and values, and user-supplied computational methods for setting and updating values. In particular:
* <ul>
*
* <li>Identity-based, Equality-based or User-definable {@link Equivalence}-based comparisons controlling membership.
*
* <li> {@linkplain SoftReference Soft}, {@linkplain WeakReference weak} or strong (regular) keys and values.
*
* <li>User-definable {@code MappingFunctions} that may be used in method
* {@link CustomConcurrentHashMap#computeIfAbsent} to atomically establish a computed value, along with
* {@code RemappingFunctions} that can be used in method {@link CustomConcurrentHashMap#compute} to atomically replace
* values.
*
* <li>Factory methods returning specialized forms for {@code int} keys and/or values, that may be more space-efficient
*
* </ul>
*
* Per-map settings are established in constructors, as in the following usages (that assume static imports to simplify
* expression of configuration parameters):
*
* <pre>
* {@code}
* identityMap = new CustomConcurrentHashMap<Person,Salary>
* (STRONG, IDENTITY, STRONG, EQUALS, 0);
* weakKeyMap = new CustomConcurrentHashMap<Person,Salary>
* (WEAK, IDENTITY, STRONG, EQUALS, 0);
* .weakKeys());
* byNameMap = new CustomConcurrentHashMap<Person,Salary>
* (STRONG,
* new Equivalence<Person>() {
* public boolean equal(Person k, Object x) {
* return x instanceof Person && k.name.equals(((Person)x).name);
* }
* public int hash(Object x) {
* return (x instanceof Person) ? ((Person)x).name.hashCode() : 0;
* }
* },
* STRONG, EQUALS, 0);
* }
* </pre>
*
* The first usage above provides a replacement for {@link java.util.IdentityHashMap}, and the second a replacement for
* {@link java.util.WeakHashMap}, adding concurrency, asynchronous cleanup, and identity-based equality for keys. The
* third usage illustrates a map with a custom Equivalence that looks only at the name field of a (fictional) Person
* class.
*
* <p>
* This class also includes nested class {@link KeySet} that provides space-efficient Set views of maps, also supporting
* method {@code intern}, which may be of use in canonicalizing elements.
*
* <p>
* When used with (Weak or Soft) Reference keys and/or values, elements that have asynchronously become {@code null} are
* treated as absent from the map and (eventually) removed from maps via a background thread common across all maps.
* Because of the potential for asynchronous clearing of References, methods such as {@code containsValue} have weaker
* guarantees than you might expect even in the absence of other explicitly concurrent operations. For example
* {@code containsValue(value)} may return true even if {@code value} is no longer available upon return from the
* method.
*
* <p>
* When Equivalences other than equality are used, the returned collections may violate the specifications of
* {@code Map} and/or {@code Set} interfaces, which mandate the use of the {@code equals} method when comparing objects.
* The methods of this class otherwise have properties similar to those of
* {@link java.util.concurrent.ConcurrentHashMap} under its default settings. To adaptively maintain semantics and
* performance under varying conditions, this class does <em>not</em> support load factor or concurrency level
* parameters. This class does not permit null keys or values. This class is serializable; however, serializing a map
* that uses soft or weak references can give unpredictable results. This class supports all optional operations of the
* {@code ConcurrentMap} interface. It supports have <i>weakly consistent iteration</i>: an iterator over one of the
* map's view collections may reflect some, all or none of the changes made to the collection after the iterator was
* created.
*
* <p>
* This class is a member of the <a href="{@docRoot}/../technotes/guides/collections/index.html"> Java Collections
* Framework</a>.
*
* @param <K>
* the type of keys maintained by this map
* @param <V>
* the type of mapped values
*/
// CHECKSTYLE:OFF
@SuppressWarnings({ "rawtypes", "unchecked", "unused", "serial" })
public class CustomConcurrentHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>, Serializable {
/**
* An Equivalence object performing {@link Object#equals} based comparisons and using {@link Object#hashCode}
* hashing
*/
public static final Equivalence<Object> EQUALS = new EquivalenceUsingEquals();
/*
* This class uses a similar approach as ConcurrentHashMap, but makes different internal tradeoffs, mainly (1) We
* use more segments, but lazily initialize them; and (2) Links connecting nodes are not immutable, enabling
* unsplicing. These two adjustments help improve concurrency in the face of heavier per-element mechanics and the
* increased load due to reference removal, while still keeping footprint etc reasonable.
*
* Additionally, because Reference keys/values may become null asynchronously, we cannot ensure snapshot integrity
* in methods such as containsValue, so do not try to obtain them (so, no modCounts etc).
*
* Also, the volatility of Segment count vs table fields are swapped, enabled by ensuring fences on new node
* assignments.
*/
/**
* An Equivalence object performing identity-based comparisons and using {@link System#identityHashCode} for hashing
*/
public static final Equivalence<Object> IDENTITY = new EquivalenceUsingIdentity();;
/** Config string for int maps */
private static final String INT_STRING = "Int";
static final int SEGMENT_BITS = 6;
static final int NSEGMENTS = 1 << SEGMENT_BITS;
// builtin equivalences
static final int SEGMENT_MASK = NSEGMENTS - 1;
static final int SEGMENT_SHIFT = 32 - SEGMENT_BITS;
static final int MAX_SEGMENT_CAPACITY = 1 << 32 - SEGMENT_BITS;
static final int MIN_SEGMENT_CAPACITY = 4;
static volatile ReferenceQueue<Object> refQueue;
static final long SEGMENT_BASE;
static final int SEGMENTS_SHIFT;
/** Config string for self-map (Set view) refs */
private static final String SELF_STRING = "Self";
private static final long serialVersionUID = 7249069246764182397L;
/** The strength of soft references */
public static final Strength SOFT = Strength.soft;
/** The strength of ordinary references */
public static final Strength STRONG = Strength.strong;
static final long TABLE_BASE;
static final int TABLE_SHIFT;
// Hardwire 64 segments
static final Unsafe UNSAFE;
/** The strength of weak references */
public static final Strength WEAK = Strength.weak;
static {
try {
UNSAFE = getUnsafe();
TABLE_BASE = UNSAFE.arrayBaseOffset(Node[].class);
int scale = UNSAFE.arrayIndexScale(Node[].class);
if ((scale & scale - 1) != 0) {
throw new Error("data type scale not a power of two");
}
TABLE_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
SEGMENT_BASE = UNSAFE.arrayBaseOffset(Segment[].class);
scale = UNSAFE.arrayIndexScale(Segment[].class);
if ((scale & scale - 1) != 0) {
throw new Error("data type scale not a power of two");
}
SEGMENTS_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (Throwable e) {
throw new RuntimeException("Could not initialize intrinsics", e);
}
}
transient Set<Map.Entry<K, V>> entrySet;
/**
* The factory for this map
*/
final NodeFactory factory;
/**
* The initial size of Segment tables when they are first constructed
*/
final int initialSegmentCapacity;
/**
* Equivalence object for keys
*/
final Equivalence<? super K> keyEquivalence;
// Cached view objects
transient Set<K> keySet;
/**
* The segments, each of which acts as a hash table
*/
transient volatile Segment[] segments;
/**
* Equivalence object for values
*/
final Equivalence<? super V> valueEquivalence;
transient Collection<V> values;
/**
* Creates a new CustomConcurrentHashMap with strong keys and values, and equality-based equivalence.
*/
public CustomConcurrentHashMap() {
this(STRONG, EQUALS, STRONG, EQUALS, 0);
}
public CustomConcurrentHashMap(Strength keyStrength, Strength valueStrength) {
this(keyStrength, EQUALS, valueStrength, EQUALS, 0);
}
/**
* Creates a new CustomConcurrentHashMap with the given parameters.
*
* @param keyStrength
* the strength for keys
* @param keyEquivalence
* the Equivalence to use for keys
* @param valueStrength
* the strength for values
* @param valueEquivalence
* the Equivalence to use for values
* @param expectedSize
* an estimate of the number of elements that will be held in the map. If no estimate is known, zero is
* an acceptable value.
*/
public CustomConcurrentHashMap(Strength keyStrength, Equivalence<? super K> keyEquivalence, Strength valueStrength,
Equivalence<? super V> valueEquivalence, int expectedSize) {
this(keyStrength.getName(), keyEquivalence, valueStrength.getName(), valueEquivalence, expectedSize);
}
/**
* Internal constructor to set factory, equivalences and segment capacities, and to create segments array.
*/
CustomConcurrentHashMap(String ks, Equivalence<? super K> keq, String vs, Equivalence<? super V> veq,
int expectedSize) {
if (keq == null || veq == null) {
throw new NullPointerException();
}
this.keyEquivalence = keq;
this.valueEquivalence = veq;
// Reflectively assemble factory name
String factoryName = CustomConcurrentHashMap.class.getName() + "$" + ks + "Key" + vs + "ValueNodeFactory";
try {
this.factory = (NodeFactory) Class.forName(factoryName).newInstance();
} catch (Exception ex) {
throw new Error("Cannot instantiate " + factoryName);
}
int es = expectedSize;
if (es == 0) {
this.initialSegmentCapacity = MIN_SEGMENT_CAPACITY;
} else {
int sc = (int) (1L + 4L * es / 3 >>> SEGMENT_BITS);
if (sc < MIN_SEGMENT_CAPACITY) {
sc = MIN_SEGMENT_CAPACITY;
}
int capacity = MIN_SEGMENT_CAPACITY; // ensure power of two
while (capacity < sc) {
capacity <<= 1;
}
if (capacity > MAX_SEGMENT_CAPACITY) {
capacity = MAX_SEGMENT_CAPACITY;
}
this.initialSegmentCapacity = capacity;
}
this.segments = new Segment[NSEGMENTS];
}
/**
* Removes all of the mappings from this map.
*/
public final void clear() {
Segment[] segs = this.segments;
for (int i = 0; i < segs.length; ++i) {
Segment seg = segs[i];
if (seg != null) {
seg.lock();
try {
seg.clearCount();
} finally {
seg.unlock();
}
}
}
}
/**
* Updates the mapping for the given key with the result of the given remappingFunction. This is equivalent to
*
* <pre>
* value = remappingFunction.remap(key, get(key));
* if (value != null)
* return put(key, value):
* else
* return remove(key);
* </pre>
*
* except that the action is performed atomically. Some attempted operations on this map by other threads may be
* blocked while computation is in progress.
*
* <p>
* Sample Usage. A remapping function can be used to perform frequency counting of words using code such as:
*
* <pre>
* map.compute(word, new RemappingFunction<String, Integer>() {
* public Integer remap(String k, Integer v) {
* return (v == null) ? 1 : v + 1;
* }
* });
* </pre>
*
* @param key
* key with which the specified value is to be associated
* @param remappingFunction
* the function to compute a value
* @return the updated value or {@code null} if the computation returned {@code null}
* @throws NullPointerException
* if the specified key or remappingFunction is null
* @throws RuntimeException
* or Error if the remappingFunction does so, in which case the mapping is left in its previous state
*/
public V compute(K key, RemappingFunction<? super K, V> remappingFunction) {
if (key == null || remappingFunction == null) {
throw new NullPointerException();
}
int hash = spreadHash(keyEquivalence.hash(key));
V value = null;
Segment seg = getSegmentForAdd(hash);
seg.lock();
try {
Node[] tab = seg.getTableForAdd(this);
int i = hash & tab.length - 1;
Node pred = null;
Node p = tab[i];
while (p != null) {
K k = (K) p.get();
if (k == key || k != null && p.getLocator() == hash && keyEquivalence.equal(k, key)) {
value = (V) p.getValue();
break;
}
pred = p;
p = p.getLinkage();
}
value = remappingFunction.remap(key, value);
if (p != null) {
if (value != null) {
p.setValue(value);
} else {
Node n = p.getLinkage();
if (pred == null) {
tab[i] = n;
} else {
pred.setLinkage(n);
}
seg.decrementCount();
}
} else if (value != null) {
Node r = factory.newNode(hash, key, value, this, tab[i]);
// Fences.preStoreFence(r);
// tab[i] = r;
storeNode(tab, i, r);
seg.incrementCount();
}
} finally {
seg.unlock();
}
return value;
}
/**
* If the specified key is not already associated with a value, computes its value using the given mappingFunction,
* and if non-null, enters it into the map. This is equivalent to
*
* <pre>
* if (map.containsKey(key))
* return map.get(key);
* value = mappingFunction.map(key);
* if (value != null)
* return map.put(key, value);
* else
* return null;
* </pre>
*
* except that the action is performed atomically. Some attempted operations on this map by other threads may be
* blocked while computation is in progress. Because this function is invoked within atomicity control, the
* computation should be short and simple. The most common usage is to construct a new object serving as an initial
* mapped value, or memoized result.
*
* @param key
* key with which the specified value is to be associated
* @param mappingFunction
* the function to compute a value
* @return the current (existing or computed) value associated with the specified key, or {@code null} if the
* computation returned {@code null}
* @throws NullPointerException
* if the specified key or mappingFunction is null
* @throws RuntimeException
* or Error if the mappingFunction does so, in which case the mapping is left unestablished
*/
public V computeIfAbsent(K key, MappingFunction<? super K, ? extends V> mappingFunction) {
if (key == null || mappingFunction == null) {
throw new NullPointerException();
}
int hash = spreadHash(keyEquivalence.hash(key));
Segment seg = getSegmentForTraversal(hash);
Node r = findNode(key, hash, seg);
V v = null;
if (r == null) {
if (seg == null) {
seg = getSegmentForAdd(hash);
}
seg.lock();
try {
r = findNode(key, hash, seg);
if (r == null || (v = (V) r.getValue()) == null) {
// Map is OK if function throws exception
v = mappingFunction.map(key);
if (v != null) {
if (r != null) {
r.setValue(v);
} else {
Node[] tab = seg.getTableForAdd(this);
int i = hash & tab.length - 1;
r = factory.newNode(hash, key, v, this, tab[i]);
// Fences.preStoreFence(r);
// tab[i] = r;
storeNode(tab, i, r);
seg.incrementCount();
}
}
}
} finally {
seg.unlock();
}
}
if (r != null && v == null) {
removeIfReclaimed(r);
}
return v;
}
/**
* Returns {@code true} if this map contains a key equivalent to the given key with respect to this map's key
* Equivalence.
*
* @param key
* possible key
* @return {@code true} if this map contains the specified key
* @throws NullPointerException
* if the specified key is null
*/
public boolean containsKey(Object key) {
if (key == null) {
throw new NullPointerException();
}
int hash = spreadHash(keyEquivalence.hash(key));
Segment seg = getSegmentForTraversal(hash);
Node r = findNode(key, hash, seg);
return r != null && r.getValue() != null;
}
/**
* Returns {@code true} if this map maps one or more keys to a value equivalent to the given value with respect to
* this map's value Equivalence. Note: This method requires a full internal traversal of the hash table, and so is
* much slower than method {@code containsKey}.
*
* @param value
* value whose presence in this map is to be tested
* @return {@code true} if this map maps one or more keys to the specified value
* @throws NullPointerException
* if the specified value is null
*/
public boolean containsValue(Object value) {
if (value == null) {
throw new NullPointerException();
}
Segment[] segs = this.segments;
for (int i = 0; i < segs.length; ++i) {
Segment seg = segs[i];
Node[] tab;
if (seg != null && (tab = seg.getTableForTraversal()) != null) {
for (int j = 0; j < tab.length; ++j) {
for (Node p = tab[j]; p != null; p = p.getLinkage()) {
V v = (V) p.getValue();
if (v == value || v != null && valueEquivalence.equal(v, value)) {
return true;
}
}
}
}
}
return false;
}
/**
* Shared implementation for put, putIfAbsent
*/
final V doPut(K key, V value, boolean onlyIfNull) {
if (key == null || value == null) {
throw new NullPointerException();
}
V oldValue = null;
int hash = spreadHash(keyEquivalence.hash(key));
Segment seg = getSegmentForAdd(hash);
seg.lock();
try {
Node r = findNode(key, hash, seg);
if (r != null) {
oldValue = (V) r.getValue();
if (!onlyIfNull || oldValue == null) {
r.setValue(value);
}
} else {
Node[] tab = seg.getTableForAdd(this);
int i = hash & tab.length - 1;
r = factory.newNode(hash, key, value, this, tab[i]);
// Fences.preStoreFence(r);
// tab[i] = r;
storeNode(tab, i, r);
seg.incrementCount();
}
} finally {
seg.unlock();
}
return oldValue;
}
/**
* Returns a {@link Set} view of the mappings contained in this map. The set is backed by the map, so changes to the
* map are reflected in the set, and vice-versa. The set supports element removal, which removes the corresponding
* mapping from the map, via the {@code Iterator.remove}, {@code Set.remove}, {@code removeAll}, {@code retainAll},
* and {@code clear} operations. It does not support the {@code add} or {@code addAll} operations.
*
* <p>
* The view's {@code iterator} is a "weakly consistent" iterator that will never throw
* {@link ConcurrentModificationException}, and guarantees to traverse elements as they existed upon construction of
* the iterator, and may (but is not guaranteed to) reflect any modifications subsequent to construction.
*/
public Set<Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
/**
* Compares the specified object with this map for equality. Returns {@code true} if the given object is also a map
* of the same size, holding keys that are equal using this Map's key Equivalence, and which map to values that are
* equal according to this Map's value equivalence.
*
* @param o
* object to be compared for equality with this map
* @return {@code true} if the specified object is equal to this map
*/
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Map)) {
return false;
}
Map<K, V> m = (Map<K, V>) o;
if (m.size() != size()) {
return false;
}
try {
Iterator<Entry<K, V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K, V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value != null) {
V mv = m.get(key);
if (mv == null) {
return false;
}
if (!valueEquivalence.equal(mv, value)) {
return false;
}
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
/**
* Returns node for key, or null if none.
*/
final Node findNode(Object key, int hash, Segment seg) {
if (seg != null) {
Node[] tab = seg.getTableForTraversal();
if (tab != null) {
Node p = tab[hash & tab.length - 1];
while (p != null) {
Object k = p.get();
if (k == key || k != null && p.getLocator() == hash && keyEquivalence.equal((K) k, key)) {
return p;
}
p = p.getLinkage();
}
}
}
return null;
}
/**
* Returns the value associated with a key equivalent to the given key with respect to this map's key Equivalence,
* or {@code null} if no such mapping exists.
*
* @param key
* possible key
* @return the value associated with the key, or {@code null} if there is no mapping
* @throws NullPointerException
* if the specified key is null
*/
public V get(Object key) {
if (key == null) {
throw new NullPointerException();
}
int hash = spreadHash(keyEquivalence.hash(key));
Segment seg = getSegmentForTraversal(hash);
Node r = findNode(key, hash, seg);
if (r == null) {
return null;
}
return (V) r.getValue();
}
/**
* Returns the segment for possibly inserting into the table associated with given hash, constructing it if
* necessary.
*
* @param hash
* the hash code for the key
* @return the segment
*/
final Segment getSegmentForAdd(int hash) {
Segment[] segs = segments;
int index = hash >>> SEGMENT_SHIFT & SEGMENT_MASK;
Segment seg = segs[index];
if (seg == null) {
synchronized (segs) {
seg = segs[index];
if (seg == null) {
seg = new Segment();
// Fences.preStoreFence(seg);
// segs[index] = seg;
storeSegment(segs, index, seg);
}
}
}
return seg;
}
/**
* Returns the segment for traversing table for key with given hash.
*
* @param hash
* the hash code for the key
* @return the segment, or null if not yet initialized
*/
final Segment getSegmentForTraversal(int hash) {
return segments[hash >>> SEGMENT_SHIFT & SEGMENT_MASK];
}
/**
* Returns the sum of the hash codes of each entry in this map's {@code entrySet()} view, which in turn are the hash
* codes computed using key and value Equivalences for this Map.
*
* @return the hash code
*/
public int hashCode() {
int h = 0;
Iterator<Entry<K, V>> i = entrySet().iterator();
while (i.hasNext()) {
h += i.next().hashCode();
}
return h;
}
/**
* Returns {@code true} if this map contains no key-value mappings.
*
* @return {@code true} if this map contains no key-value mappings
*/
public boolean isEmpty() {
final Segment[] segs = this.segments;
for (int i = 0; i < segs.length; ++i) {
Segment seg = segs[i];
if (seg != null && seg.getTableForTraversal() != null && seg.count != 0) {
return false;
}
}
return true;
}
final KeyIterator keyIterator() { // needed by Set
return new KeyIterator();
}
/**
* Returns a {@link Set} view of the keys contained in this map. The set is backed by the map, so changes to the map
* are reflected in the set, and vice-versa. The set supports element removal, which removes the corresponding
* mapping from this map, via the {@code Iterator.remove}, {@code Set.remove}, {@code removeAll}, {@code retainAll},
* and {@code clear} operations. It does not support the {@code add} or {@code addAll} operations.
*
* <p>
* The view's {@code iterator} is a "weakly consistent" iterator that will never throw
* {@link ConcurrentModificationException}, and guarantees to traverse elements as they existed upon construction of
* the iterator, and may (but is not guaranteed to) reflect any modifications subsequent to construction.
*/
public Set<K> keySet() {
Set<K> ks = keySet;
return ks != null ? ks : (keySet = new KeySetView());
}
/**
* Maps the specified key to the specified value in this map.
*
* @param key
* key with which the specified value is to be associated
* @param value
* value to be associated with the specified key
* @return the previous value associated with {@code key}, or {@code null} if there was no mapping for {@code key}
* @throws NullPointerException
* if the specified key or value is null
*/
public V put(K key, V value) {
return doPut(key, value, false);
}
/**
* Copies all of the mappings from the specified map to this one. These mappings replace any mappings that this map
* had for any of the keys currently in the specified map.
*
* @param m
* mappings to be stored in this map
*/
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
put(e.getKey(), e.getValue());
}
}
/**
* {@inheritDoc}
*
* @return the previous value associated with the specified key, or {@code null} if there was no mapping for the key
* @throws NullPointerException
* if the specified key or value is null
*/
public V putIfAbsent(K key, V value) {
return doPut(key, value, true);
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*
* @param s
* the stream
* @throws IOException
* ioe
* @throws ClassNotFoundException
* ioe
*/
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
this.segments = new Segment[NSEGMENTS];
for (;;) {
K key = (K) s.readObject();
V value = (V) s.readObject();
if (key == null) {
break;
}
put(key, value);
}
}
/**
* Removes the mapping for the specified key.
*
* @param key
* the key to remove
* @return the previous value associated with {@code key}, or {@code null} if there was no mapping for {@code key}
* @throws NullPointerException
* if the specified key is null
*/
public V remove(Object key) {
if (key == null) {
throw new NullPointerException();
}
V oldValue = null;
int hash = spreadHash(keyEquivalence.hash(key));
Segment seg = getSegmentForTraversal(hash);
if (seg != null) {
seg.lock();
try {
Node[] tab = seg.getTableForTraversal();
if (tab != null) {
int i = hash & tab.length - 1;
Node pred = null;
Node p = tab[i];
while (p != null) {
Node n = p.getLinkage();
Object k = p.get();
if (k == key || k != null && p.getLocator() == hash && keyEquivalence.equal((K) k, key)) {
oldValue = (V) p.getValue();
if (pred == null) {
tab[i] = n;
} else {
pred.setLinkage(n);
}
seg.decrementCount();
break;
}
pred = p;
p = n;
}
}
} finally {
seg.unlock();
}
}
return oldValue;
}
/**
* {@inheritDoc}
*
* @throws NullPointerException
* if the specified key is null
*/
public boolean remove(Object key, Object value) {
if (key == null) {
throw new NullPointerException();
}
if (value == null) {
return false;
}
boolean removed = false;
int hash = spreadHash(keyEquivalence.hash(key));
Segment seg = getSegmentForTraversal(hash);
if (seg != null) {
seg.lock();
try {
Node[] tab = seg.getTableForTraversal();
if (tab != null) {
int i = hash & tab.length - 1;
Node pred = null;
Node p = tab[i];
while (p != null) {
Node n = p.getLinkage();
Object k = p.get();
if (k == key || k != null && p.getLocator() == hash && keyEquivalence.equal((K) k, key)) {
V v = (V) p.getValue();
if (v == value || v != null && valueEquivalence.equal(v, value)) {
if (pred == null) {
tab[i] = n;
} else {
pred.setLinkage(n);
}
seg.decrementCount();
removed = true;
}
break;
}
pred = p;
p = n;
}
}
} finally {
seg.unlock();
}
}
return removed;
}
/**
* Removes node if its key or value are null.
*/
final void removeIfReclaimed(Node r) {
int hash = r.getLocator();
Segment seg = getSegmentForTraversal(hash);
if (seg != null) {
seg.lock();
try {
Node[] tab = seg.getTableForTraversal();
if (tab != null) {
// remove all reclaimed in list
int i = hash & tab.length - 1;
Node pred = null;
Node p = tab[i];
while (p != null) {
Node n = p.getLinkage();
if (p.get() != null && p.getValue() != null) {
pred = p;
p = n;
} else {
if (pred == null) {
tab[i] = n;
} else {
pred.setLinkage(n);
}
seg.decrementCount();
p = n;
}
}
}
} finally {
seg.unlock();
}
}
}
/**
* {@inheritDoc}
*
* @throws NullPointerException
* if any of the arguments are null
*/
public V replace(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException();
}
V oldValue = null;
int hash = spreadHash(keyEquivalence.hash(key));
Segment seg = getSegmentForTraversal(hash);
if (seg != null) {
seg.lock();
try {
Node r = findNode(key, hash, seg);
if (r != null) {
oldValue = (V) r.getValue();
r.setValue(value);
}
} finally {
seg.unlock();
}
}
return oldValue;
}
/**
* {@inheritDoc}
*
* @return the previous value associated with the specified key, or {@code null} if there was no mapping for the key
* @throws NullPointerException
* if the specified key or value is null
*/
public boolean replace(K key, V oldValue, V newValue) {
if (key == null || oldValue == null || newValue == null) {
throw new NullPointerException();
}
boolean replaced = false;
int hash = spreadHash(keyEquivalence.hash(key));
Segment seg = getSegmentForTraversal(hash);
if (seg != null) {
seg.lock();
try {
Node r = findNode(key, hash, seg);
if (r != null) {
V v = (V) r.getValue();
if (v == oldValue || v != null && valueEquivalence.equal(v, oldValue)) {
r.setValue(newValue);
replaced = true;
}
}
} finally {
seg.unlock();
}
}
return replaced;
}
/**
* Returns the number of key-value mappings in this map. If the map contains more than {@code Integer.MAX_VALUE}
* elements, returns {@code Integer.MAX_VALUE}.
*
* @return the number of key-value mappings in this map
*/
public int size() {
long sum = 0;
final Segment[] segs = this.segments;
for (int i = 0; i < segs.length; ++i) {
Segment seg = segs[i];
if (seg != null && seg.getTableForTraversal() != null) {
sum += seg.count;
}
}
return sum >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) sum;
}
/**
* Returns a {@link Collection} view of the values contained in this map. The collection is backed by the map, so
* changes to the map are reflected in the collection, and vice-versa. The collection supports element removal,
* which removes the corresponding mapping from this map, via the {@code Iterator.remove}, {@code Collection.remove}
* , {@code removeAll}, {@code retainAll}, and {@code clear} operations. It does not support the {@code add} or
* {@code addAll} operations.
*
* <p>
* The view's {@code iterator} is a "weakly consistent" iterator that will never throw
* {@link ConcurrentModificationException}, and guarantees to traverse elements as they existed upon construction of
* the iterator, and may (but is not guaranteed to) reflect any modifications subsequent to construction.
*/
public Collection<V> values() {
Collection<V> vs = values;
return vs != null ? vs : (values = new Values());
}
/**
* Saves the state of the instance to a stream (i.e., serializes it).
*
* @param s
* the stream
* @serialData the key (Object) and value (Object) for each key-value mapping, followed by a null pair. The
* key-value mappings are emitted in no particular order.
* @throws IOException
* ioe
*/
private void writeObject(java.io.ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
for (Map.Entry<K, V> e : entrySet()) {
s.writeObject(e.getKey());
s.writeObject(e.getValue());
}
s.writeObject(null);
s.writeObject(null);
}
/**
* Returns a queue that may be used as the ReferenceQueue argument to {@link java.lang.ref.Reference} constructors
* to arrange removal of reclaimed nodes from maps via a background thread.
*
* @return the reference queue associated with the background cleanup thread
*/
static ReferenceQueue<Object> getReclamationQueue() {
ReferenceQueue<Object> q = refQueue;
if (q != null) {
return q;
} else {
return startReclamation();
}
}
/**
* Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. Replace with a simple call to
* Unsafe.getUnsafe when integrating into a jdk.
*
* @return a sun.misc.Unsafe
*/
private static sun.misc.Unsafe getUnsafe() {
try {
return sun.misc.Unsafe.getUnsafe();
} catch (SecurityException tryReflectionInstead) {}
try {
return java.security.AccessController
.doPrivileged(new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() {
public sun.misc.Unsafe run() throws Exception {
Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
for (java.lang.reflect.Field f : k.getDeclaredFields()) {
f.setAccessible(true);
Object x = f.get(null);
if (k.isInstance(x)) {
return k.cast(x);
}
}
throw new NoSuchFieldError("the Unsafe");
}
});
} catch (java.security.PrivilegedActionException e) {
throw new RuntimeException("Could not initialize intrinsics", e.getCause());
}
}
/**
* Returns a new map using Integer keys and values.
*
* @param expectedSize
* an estimate of the number of elements that will be held in the map. If no estimate is known, zero is
* an acceptable value.
* @return the map
*/
public static CustomConcurrentHashMap<Integer, Integer> newIntKeyIntValueMap(int expectedSize) {
return new CustomConcurrentHashMap<Integer, Integer>(INT_STRING, EQUALS, INT_STRING, EQUALS, expectedSize);
}
/**
* Returns a new map using Integer keys and the given value parameters.
*
* @param valueStrength
* the strength for values
* @param valueEquivalence
* the Equivalence to use for values
* @param expectedSize
* an estimate of the number of elements that will be held in the map. If no estimate is known, zero is
* an acceptable value.
* @param <ValueType>
* the type of values
* @return the map
*/
public static <ValueType> CustomConcurrentHashMap<Integer, ValueType> newIntKeyMap(Strength valueStrength,
Equivalence<? super ValueType> valueEquivalence, int expectedSize) {
return new CustomConcurrentHashMap<Integer, ValueType>(INT_STRING, EQUALS, valueStrength.getName(),
valueEquivalence, expectedSize);
}
/**
* Returns a new map using the given key parameters and Integer values.
*
* @param keyStrength
* the strength for keys
* @param keyEquivalence
* the Equivalence to use for keys
* @param expectedSize
* an estimate of the number of elements that will be held in the map. If no estimate is known, zero is
* an acceptable value.
* @return the map
* @param <KeyType>
* the type of keys
*/
public static <KeyType> CustomConcurrentHashMap<KeyType, Integer> newIntValueMap(Strength keyStrength,
Equivalence<? super KeyType> keyEquivalence, int expectedSize) {
return new CustomConcurrentHashMap<KeyType, Integer>(keyStrength.getName(), keyEquivalence, INT_STRING, EQUALS,
expectedSize);
}
/**
* Applies a supplemental hash function to a given hashCode, which defends against poor quality hash functions. This
* is critical because we use power-of-two length hash tables, that otherwise encounter collisions for hashCodes
* that do not differ in lower or upper bits.
*/
static int spreadHash(int h) {
// Spread bits to regularize both segment and index locations,
// using variant of single-word Wang/Jenkins hash.
h += h << 15 ^ 0xffffcd7d;
h ^= h >>> 10;
h += h << 3;
h ^= h >>> 6;
h += (h << 2) + (h << 14);
return h ^ h >>> 16;
}
static synchronized ReferenceQueue<Object> startReclamation() {
ReferenceQueue<Object> q = refQueue;
if (q == null) {
refQueue = q = new ReferenceQueue<Object>();
new ReclamationThread(q).start();
}
return q;
}
// Fenced store into segment table array. Unneeded when we have Fences
static final void storeNode(Node[] table, int i, Node r) {
long nodeOffset = ((long) i << TABLE_SHIFT) + TABLE_BASE;
UNSAFE.putOrderedObject(table, nodeOffset, r);
}
static final void storeSegment(Segment[] segs, int i, Segment s) {
long segmentOffset = ((long) i << SEGMENTS_SHIFT) + SEGMENT_BASE;
UNSAFE.putOrderedObject(segs, segmentOffset, s);
}
static class EmbeddedSoftReference extends SoftReference implements Reclaimable {
final Reclaimable outer;
EmbeddedSoftReference(Object x, Reclaimable outer) {
super(x, getReclamationQueue());
this.outer = outer;
}
public final void onReclamation() {
clear();
outer.onReclamation();
}
}
static class EmbeddedWeakReference extends WeakReference implements Reclaimable {
final Reclaimable outer;
EmbeddedWeakReference(Object x, Reclaimable outer) {
super(x, getReclamationQueue());
this.outer = outer;
}
public final void onReclamation() {
clear();
outer.onReclamation();
}
}
// overridden AbstractMap methods
final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K, V>> {
public Map.Entry<K, V> next() {
return super.nextEntry();
}
}
final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
public void clear() {
CustomConcurrentHashMap.this.clear();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry)) {
return false;
}
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
V v = CustomConcurrentHashMap.this.get(e.getKey());
return v != null && valueEquivalence.equal(v, e.getValue());
}
public boolean isEmpty() {
return CustomConcurrentHashMap.this.isEmpty();
}
public Iterator<Map.Entry<K, V>> iterator() {
return new EntryIterator();
}
public boolean remove(Object o) {
if (!(o instanceof Map.Entry)) {
return false;
}
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
return CustomConcurrentHashMap.this.remove(e.getKey(), e.getValue());
}
public int size() {
return CustomConcurrentHashMap.this.size();
}
}
/**
* An object performing equality comparisons, along with a hash function consistent with this comparison. The type
* signatures of the methods of this interface reflect those of {@link java.util.Map}: While only elements of
* {@code K} may be entered into a Map, any {@code Object} may be tested for membership. Note that the performance
* of hash maps is heavily dependent on the quality of hash functions.
*/
public interface Equivalence<K> {
/**
* Returns true if the given objects are considered equal. This function must obey an equivalence relation:
* equal(a, a) is always true, equal(a, b) implies equal(b, a), and (equal(a, b) && equal(b, c) implies
* equal(a, c). Note that the second argument need not be known to have the same declared type as the first.
*
* @param key
* a key in, or being placed in, the map
* @param x
* an object queried for membership
* @return true if considered equal
*/
boolean equal(K key, Object x);
/**
* Returns a hash value such that equal(a, b) implies hash(a)==hash(b).
*
* @param x
* an object queried for membership
* @return a hash value
*/
int hash(Object x);
}
static final class EquivalenceUsingEquals implements Equivalence<Object>, Serializable {
private static final long serialVersionUID = 7259069247764182397L;
public boolean equal(Object a, Object b) {
return a.equals(b);
}
public int hash(Object a) {
return a.hashCode();
}
}
static final class EquivalenceUsingIdentity implements Equivalence<Object>, Serializable {
private static final long serialVersionUID = 7259069246764182397L;
public boolean equal(Object a, Object b) {
return a == b;
}
public int hash(Object a) {
return System.identityHashCode(a);
}
}
// Reference queue mechanics for reclaimable nodes
abstract class HashIterator {
Node[] currentTable;
Object lastKey; // for remove()
Object nextKey; // key of nextNode
Node nextNode;
int nextSegmentIndex;
int nextTableIndex;
Object nextValue; // value of nextNode
HashIterator() {
nextSegmentIndex = segments.length - 1;
nextTableIndex = -1;
advance();
}
final void advance() {
lastKey = nextKey;
if (nextNode != null) {
nextNode = nextNode.getLinkage();
}
for (;;) {
if (nextNode != null) {
if ((nextKey = nextNode.get()) != null && (nextValue = nextNode.getValue()) != null) {
return;
}
Node n = nextNode.getLinkage();
removeIfReclaimed(nextNode);
nextNode = n;
} else if (nextTableIndex >= 0) {
nextNode = currentTable[nextTableIndex--];
} else if (nextSegmentIndex >= 0) {
Segment seg = segments[nextSegmentIndex--];
Node[] t;
if (seg != null && (t = seg.getTableForTraversal()) != null) {
currentTable = t;
nextTableIndex = t.length - 1;
}
} else {
nextKey = null;
nextValue = null;
return;
}
}
}
public boolean hasNext() {
return nextNode != null;
}
final Map.Entry<K, V> nextEntry() {
if (nextNode == null) {
throw new NoSuchElementException();
}
WriteThroughEntry e = new WriteThroughEntry((K) nextKey, (V) nextValue);
advance();
return e;
}
final K nextKey() {
if (nextNode == null) {
throw new NoSuchElementException();
}
Object k = nextKey;
advance();
return (K) k;
}
final V nextValue() {
if (nextNode == null) {
throw new NoSuchElementException();
}
Object v = nextValue;
advance();
return (V) v;
}
public void remove() {
if (lastKey == null) {
throw new IllegalStateException();
}
CustomConcurrentHashMap.this.remove(lastKey);
lastKey = null;
}
}
abstract static class IntKeyIntValueNode extends IntKeyNode {
volatile int value;
IntKeyIntValueNode(int locator, Object key, Object value) {
super(locator, key);
this.value = ((Integer) value).intValue();
}
public final Object getValue() {
return Integer.valueOf(value);
}
public final void onReclamation() {}
public final void setValue(Object value) {
this.value = ((Integer) value).intValue();
}
}
static final class IntKeyIntValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalIntKeyIntValueNode(locator, key, value);
} else {
return new LinkedIntKeyIntValueNode(locator, key, value, linkage);
}
}
}
abstract static class IntKeyNode implements Node {
final int key;
IntKeyNode(int locator, Object key) {
this.key = ((Integer) key).intValue();
}
public final Object get() {
return Integer.valueOf(key);
}
public int getLocator() {
return spreadHash(key);
}
}
// classes extending Weak/soft refs embedded in reclaimable nodes
abstract static class IntKeySelfValueNode extends IntKeyNode {
IntKeySelfValueNode(int locator, Object key) {
super(locator, key);
}
public final Object getValue() {
return Integer.valueOf(key);
}
public final void onReclamation() {}
public final void setValue(Object value) {}
}
static final class IntKeySelfValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalIntKeySelfValueNode(locator, key);
} else {
return new LinkedIntKeySelfValueNode(locator, key, linkage);
}
}
}
// builtin mapping node classes
// Strong Keys
abstract static class IntKeySoftValueNode extends IntKeyNode {
final CustomConcurrentHashMap cchm;
volatile EmbeddedSoftReference valueRef;
IntKeySoftValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key);
this.cchm = cchm;
if (value != null) {
this.valueRef = new EmbeddedSoftReference(value, this);
}
}
public final Object getValue() {
EmbeddedSoftReference vr = valueRef;
return vr == null ? null : vr.get();
}
public final void onReclamation() {
cchm.removeIfReclaimed(this);
}
public final void setValue(Object value) {
if (value == null) {
valueRef = null;
} else {
valueRef = new EmbeddedSoftReference(value, this);
}
}
}
static final class IntKeySoftValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalIntKeySoftValueNode(locator, key, value, cchm);
} else {
return new LinkedIntKeySoftValueNode(locator, key, value, cchm, linkage);
}
}
}
abstract static class IntKeyStrongValueNode extends IntKeyNode {
volatile Object value;
IntKeyStrongValueNode(int locator, Object key, Object value) {
super(locator, key);
this.value = value;
}
public final Object getValue() {
return value;
}
public final void onReclamation() {}
public final void setValue(Object value) {
this.value = value;
}
}
static final class IntKeyStrongValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalIntKeyStrongValueNode(locator, key, value);
} else {
return new LinkedIntKeyStrongValueNode(locator, key, value, linkage);
}
}
}
abstract static class IntKeyWeakValueNode extends IntKeyNode {
final CustomConcurrentHashMap cchm;
volatile EmbeddedWeakReference valueRef;
IntKeyWeakValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key);
this.cchm = cchm;
if (value != null) {
this.valueRef = new EmbeddedWeakReference(value, this);
}
}
public final Object getValue() {
EmbeddedWeakReference vr = valueRef;
return vr == null ? null : vr.get();
}
public final void onReclamation() {
cchm.removeIfReclaimed(this);
}
public final void setValue(Object value) {
if (value == null) {
valueRef = null;
} else {
valueRef = new EmbeddedWeakReference(value, this);
}
}
}
static final class IntKeyWeakValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalIntKeyWeakValueNode(locator, key, value, cchm);
} else {
return new LinkedIntKeyWeakValueNode(locator, key, value, cchm, linkage);
}
}
}
final class KeyIterator extends HashIterator implements Iterator<K> {
public K next() {
return super.nextKey();
}
}
/**
* A hash-based set with properties identical to those of {@code Collections.newSetFromMap} applied to a
* {@code CustomConcurrentHashMap}, but possibly more space-efficient. The set does not permit null elements. The
* set is serializable; however, serializing a set that uses soft or weak references can give unpredictable results.
*/
public static class KeySet<K> extends AbstractSet<K> implements Set<K>, Serializable {
final CustomConcurrentHashMap<K, K> cchm;
/**
* Creates a set with the given parameters.
*
* @param strength
* the strength of elements
* @param equivalence
* the Equivalence to use
* @param expectedSize
* an estimate of the number of elements that will be held in the set. If no estimate is known, zero
* is an acceptable value.
*/
public KeySet(Strength strength, Equivalence<? super K> equivalence, int expectedSize) {
this.cchm = new CustomConcurrentHashMap<K, K>(strength.getName(), equivalence, SELF_STRING, equivalence,
expectedSize);
}
/**
* Adds the specified element to this set if there is not already an element equivalent to the given element
* with respect to this set's Equivalence.
*
* @param e
* element to be added to this set
* @return {@code true} if this set did not already contain the specified element
*/
public boolean add(K e) {
return cchm.doPut(e, e, true) != null;
}
/**
* Removes all of the elements from this set.
*/
public void clear() {
cchm.clear();
}
/**
* Returns {@code true} if this set contains an element equivalent to the given element with respect to this
* set's Equivalence.
*
* @param o
* element whose presence in this set is to be tested
* @return {@code true} if this set contains the specified element
*/
public boolean contains(Object o) {
return cchm.containsKey(o);
}
/**
* Returns the sum of the hash codes of each element, as computed by this set's Equivalence.
*
* @return the hash code
*/
public int hashCode() {
int h = 0;
Iterator<K> i = iterator();
while (i.hasNext()) {
h += cchm.keyEquivalence.hash(i.next());
}
return h;
}
/**
* Returns an element equivalent to the given element with respect to this set's Equivalence, if such an element
* exists, else adds and returns the given element.
*
* @param e
* the element
* @return e, or an element equivalent to e
*/
public K intern(K e) {
K oldElement = cchm.doPut(e, e, true);
return oldElement != null ? oldElement : e;
}
/**
* Returns {@code true} if this set contains no elements.
*
* @return {@code true} if this set contains no elements
*/
public boolean isEmpty() {
return cchm.isEmpty();
}
/**
* Returns a <i>weakly consistent iterator</i> over the elements in this set, that may reflect some, all or none
* of the changes made to the set after the iterator was created.
*
* @return an Iterator over the elements in this set
*/
public Iterator<K> iterator() {
return cchm.keyIterator();
}
/**
* Removes an element equivalent to the given element with respect to this set's Equivalence, if one is present.
*
* @param o
* object to be removed from this set, if present
* @return {@code true} if the set contained the specified element
*/
public boolean remove(Object o) {
return cchm.remove(o) != null;
}
/**
* Returns the number of elements in this set (its cardinality).
*
* @return the number of elements in this set (its cardinality)
*/
public int size() {
return cchm.size();
}
}
final class KeySetView extends AbstractSet<K> {
public void clear() {
CustomConcurrentHashMap.this.clear();
}
public boolean contains(Object o) {
return CustomConcurrentHashMap.this.containsKey(o);
}
public boolean isEmpty() {
return CustomConcurrentHashMap.this.isEmpty();
}
public Iterator<K> iterator() {
return new KeyIterator();
}
public boolean remove(Object o) {
return CustomConcurrentHashMap.this.remove(o) != null;
}
public int size() {
return CustomConcurrentHashMap.this.size();
}
}
// ...
static final class LinkedIntKeyIntValueNode extends IntKeyIntValueNode {
volatile Node linkage;
LinkedIntKeyIntValueNode(int locator, Object key, Object value, Node linkage) {
super(locator, key, value);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedIntKeySelfValueNode extends IntKeySelfValueNode {
volatile Node linkage;
LinkedIntKeySelfValueNode(int locator, Object key, Node linkage) {
super(locator, key);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedIntKeySoftValueNode extends IntKeySoftValueNode {
volatile Node linkage;
LinkedIntKeySoftValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, value, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedIntKeyStrongValueNode extends IntKeyStrongValueNode {
volatile Node linkage;
LinkedIntKeyStrongValueNode(int locator, Object key, Object value, Node linkage) {
super(locator, key, value);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
// ...
static final class LinkedIntKeyWeakValueNode extends IntKeyWeakValueNode {
volatile Node linkage;
LinkedIntKeyWeakValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, value, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedSoftKeyIntValueNode extends SoftKeyIntValueNode {
volatile Node linkage;
LinkedSoftKeyIntValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, value, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedSoftKeySelfValueNode extends SoftKeySelfValueNode {
volatile Node linkage;
LinkedSoftKeySelfValueNode(int locator, Object key, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedSoftKeySoftValueNode extends SoftKeySoftValueNode {
volatile Node linkage;
LinkedSoftKeySoftValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, value, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedSoftKeyStrongValueNode extends SoftKeyStrongValueNode {
volatile Node linkage;
LinkedSoftKeyStrongValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, value, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedSoftKeyWeakValueNode extends SoftKeyWeakValueNode {
volatile Node linkage;
LinkedSoftKeyWeakValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, value, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedStrongKeyIntValueNode extends StrongKeyIntValueNode {
volatile Node linkage;
LinkedStrongKeyIntValueNode(int locator, Object key, Object value, Node linkage) {
super(locator, key, value);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedStrongKeySelfValueNode extends StrongKeySelfValueNode {
volatile Node linkage;
LinkedStrongKeySelfValueNode(int locator, Object key, Node linkage) {
super(locator, key);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
// Weak keys
static final class LinkedStrongKeySoftValueNode extends StrongKeySoftValueNode {
volatile Node linkage;
LinkedStrongKeySoftValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, value, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedStrongKeyStrongValueNode extends StrongKeyStrongValueNode {
volatile Node linkage;
LinkedStrongKeyStrongValueNode(int locator, Object key, Object value, Node linkage) {
super(locator, key, value);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedStrongKeyWeakValueNode extends StrongKeyWeakValueNode {
volatile Node linkage;
LinkedStrongKeyWeakValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, value, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedWeakKeyIntValueNode extends WeakKeyIntValueNode {
volatile Node linkage;
LinkedWeakKeyIntValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, value, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedWeakKeySelfValueNode extends WeakKeySelfValueNode {
volatile Node linkage;
LinkedWeakKeySelfValueNode(int locator, Object key, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedWeakKeySoftValueNode extends WeakKeySoftValueNode {
volatile Node linkage;
LinkedWeakKeySoftValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, value, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedWeakKeyStrongValueNode extends WeakKeyStrongValueNode {
volatile Node linkage;
LinkedWeakKeyStrongValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, value, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
static final class LinkedWeakKeyWeakValueNode extends WeakKeyWeakValueNode {
volatile Node linkage;
LinkedWeakKeyWeakValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
super(locator, key, value, cchm);
this.linkage = linkage;
}
public Node getLinkage() {
return linkage;
}
public void setLinkage(Node linkage) {
this.linkage = linkage;
}
}
/**
* A function computing a mapping from the given key to a value, or {@code null} if there is no mapping.
*/
public interface MappingFunction<K, V> {
/**
* Returns a value for the given key, or null if there is no mapping. If this function throws an (unchecked)
* exception, the exception is rethrown to its caller, and no mapping is recorded. Because this function is
* invoked within atomicity control, the computation should be short and simple. The most common usage is to
* construct a new object serving as an initial mapped value.
*
* @param key
* the (non-null) key
* @return a value, or null if none
*/
V map(K key);
}
/**
* An object maintaining a key-value mapping. Nodes provide methods that are intended to used <em>only</em> by the
* map creating the node. This includes methods used solely for internal bookkeeping by maps, that must be treating
* opaquely by implementation classes. (This requirement stems from the fact that concrete implementations may be
* required to subclass {@link java.lang.ref.Reference} or other classes, so a base class cannot be established.)
*
* This interface uses raw types as the lesser of evils. Otherwise we'd encounter almost as many unchecked casts
* when nodes are used across sets, etc.
*/
interface Node extends Reclaimable {
/**
* Returns the key established during the creation of this node. Note: This method is named "get" rather than
* "getKey" to simplify usage of Reference keys.
*
* @return the key
*/
Object get();
/**
* Returns the linkage established during the creation of this node or, if since updated, the linkage set by the
* most recent call to setLinkage.
*
* @return the linkage
*/
Node getLinkage();
/**
* Returns the locator established during the creation of this node.
*
* @return the locator
*/
int getLocator();
/**
* Returns the value established during the creation of this node or, if since updated, the value set by the
* most recent call to setValue, or throws an exception if value could not be computed.
*
* @return the value
* @throws RuntimeException
* or Error if computeValue failed
*/
Object getValue();
/**
* Records the linkage to be returned by the next call to getLinkage.
*
* @param linkage
* the linkage
*/
void setLinkage(Node linkage);
/**
* Nodes the value to be returned by the next call to getValue.
*
* @param value
* the value
*/
void setValue(Object value);
}
/**
* A factory for Nodes.
*/
interface NodeFactory extends Serializable {
/**
* Creates and returns a Node using the given parameters.
*
* @param locator
* an opaque immutable locator for this node
* @param key
* the (non-null) immutable key
* @param value
* the (non-null) volatile value
* @param cchm
* the table creating this node
* @param linkage
* an opaque volatile linkage for maintaining this node
*/
Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage);
}
/**
* An object that may be subject to cleanup operations when removed from a {@link java.lang.ref.ReferenceQueue}
*/
interface Reclaimable {
/**
* The action taken upon removal of this object from a ReferenceQueue.
*/
void onReclamation();
}
static final class ReclamationThread extends Thread {
final ReferenceQueue<Object> queue;
ReclamationThread(ReferenceQueue<Object> q) {
this.queue = q;
setDaemon(true);
}
public void run() {
ReferenceQueue<Object> q = queue;
for (;;) {
try {
Reference<?> r = q.remove();
if (r instanceof Reclaimable) {
((Reclaimable) r).onReclamation();
}
} catch (InterruptedException e) {
/* ignore */
}
}
}
}
/**
* A function computing a new mapping from the given key and its current value to a new value, or {@code null} if
* there is no mapping.
*/
public interface RemappingFunction<K, V> {
/**
* Returns a new value for the given key and its current, or null if there is no mapping.
*
* @param key
* the key
* @param value
* the current value, or null if none
* @return a value, or null if none
*/
V remap(K key, V value);
}
/**
* Each Segment holds a count and table corresponding to a segment of the table. This class contains only those
* methods for directly assigning these fields, which must only be called while holding locks.
*/
static final class Segment extends ReentrantLock {
int count;
volatile Node[] table;
void clearCount() {
count = 0;
table = null;
}
void decrementCount() {
if (--count == 0) {
table = null;
}
}
Node[] getTableForAdd(CustomConcurrentHashMap cchm) {
int len;
Node[] tab = table;
if (tab == null || // 3/4 threshold
(len = tab.length) - (len >>> 2) < count) {
return resizeTable(cchm);
} else {
return tab;
}
}
Node[] getTableForTraversal() {
return table;
}
void incrementCount() {
++count;
}
/**
* See the similar code in ConcurrentHashMap for explanation.
*/
Node[] resizeTable(CustomConcurrentHashMap cchm) {
Node[] oldTable = table;
if (oldTable == null) {
return table = new Node[cchm.initialSegmentCapacity];
}
int oldCapacity = oldTable.length;
if (oldCapacity >= MAX_SEGMENT_CAPACITY) {
return oldTable;
}
Node[] newTable = new Node[oldCapacity << 1];
int sizeMask = newTable.length - 1;
NodeFactory fac = cchm.factory;
for (int i = 0; i < oldCapacity; i++) {
Node e = oldTable[i];
if (e != null) {
Node next = e.getLinkage();
int idx = e.getLocator() & sizeMask;
// Single node on list
if (next == null) {
newTable[idx] = e;
} else {
// Reuse trailing consecutive sequence at same slot
Node lastRun = e;
int lastIdx = idx;
for (Node last = next; last != null; last = last.getLinkage()) {
int k = last.getLocator() & sizeMask;
if (k != lastIdx) {
lastIdx = k;
lastRun = last;
}
}
newTable[lastIdx] = lastRun;
// Clone all remaining nodes
for (Node p = e; p != lastRun; p = p.getLinkage()) {
int ph = p.getLocator();
int k = ph & sizeMask;
Object pk = p.get();
Object pv;
if (pk == null || (pv = p.getValue()) == null) {
--count;
} else {
newTable[k] = fac.newNode(ph, pk, pv, cchm, newTable[k]);
}
}
}
}
}
return table = newTable;
}
}
abstract static class SoftKeyIntValueNode extends SoftKeyNode {
volatile int value;
SoftKeyIntValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, cchm);
this.value = ((Integer) value).intValue();
}
public final Object getValue() {
return Integer.valueOf(value);
}
public final void setValue(Object value) {
this.value = ((Integer) value).intValue();
}
}
static final class SoftKeyIntValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalSoftKeyIntValueNode(locator, key, value, cchm);
} else {
return new LinkedSoftKeyIntValueNode(locator, key, value, cchm, linkage);
}
}
}
abstract static class SoftKeyNode extends SoftReference implements Node {
final CustomConcurrentHashMap cchm;
final int locator;
SoftKeyNode(int locator, Object key, CustomConcurrentHashMap cchm) {
super(key, getReclamationQueue());
this.locator = locator;
this.cchm = cchm;
}
public int getLocator() {
return locator;
}
public final void onReclamation() {
clear();
cchm.removeIfReclaimed(this);
}
}
abstract static class SoftKeySelfValueNode extends SoftKeyNode {
SoftKeySelfValueNode(int locator, Object key, CustomConcurrentHashMap cchm) {
super(locator, key, cchm);
}
public final Object getValue() {
return get();
}
public final void setValue(Object value) {}
}
static final class SoftKeySelfValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalSoftKeySelfValueNode(locator, key, cchm);
} else {
return new LinkedSoftKeySelfValueNode(locator, key, cchm, linkage);
}
}
}
abstract static class SoftKeySoftValueNode extends SoftKeyNode {
volatile EmbeddedSoftReference valueRef;
SoftKeySoftValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, cchm);
if (value != null) {
this.valueRef = new EmbeddedSoftReference(value, this);
}
}
public final Object getValue() {
EmbeddedSoftReference vr = valueRef;
return vr == null ? null : vr.get();
}
public final void setValue(Object value) {
if (value == null) {
valueRef = null;
} else {
valueRef = new EmbeddedSoftReference(value, this);
}
}
}
// Soft keys
static final class SoftKeySoftValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalSoftKeySoftValueNode(locator, key, value, cchm);
} else {
return new LinkedSoftKeySoftValueNode(locator, key, value, cchm, linkage);
}
}
}
abstract static class SoftKeyStrongValueNode extends SoftKeyNode {
volatile Object value;
SoftKeyStrongValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, cchm);
this.value = value;
}
public final Object getValue() {
return value;
}
public final void setValue(Object value) {
this.value = value;
}
}
static final class SoftKeyStrongValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalSoftKeyStrongValueNode(locator, key, value, cchm);
} else {
return new LinkedSoftKeyStrongValueNode(locator, key, value, cchm, linkage);
}
}
}
abstract static class SoftKeyWeakValueNode extends SoftKeyNode {
volatile EmbeddedWeakReference valueRef;
SoftKeyWeakValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, cchm);
if (value != null) {
this.valueRef = new EmbeddedWeakReference(value, this);
}
}
public final Object getValue() {
EmbeddedWeakReference vr = valueRef;
return vr == null ? null : vr.get();
}
public final void setValue(Object value) {
if (value == null) {
valueRef = null;
} else {
valueRef = new EmbeddedWeakReference(value, this);
}
}
}
static final class SoftKeyWeakValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalSoftKeyWeakValueNode(locator, key, value, cchm);
} else {
return new LinkedSoftKeyWeakValueNode(locator, key, value, cchm, linkage);
}
}
}
/**
* The strength of keys and values that may be held by maps. strong denotes ordinary objects. weak and soft denote
* the corresponding {@link java.lang.ref.Reference} types.
*/
public enum Strength {
soft("Soft"), strong("Strong"), weak("Weak");
private final String name;
Strength(String name) {
this.name = name;
}
String getName() {
return name;
}
}
abstract static class StrongKeyIntValueNode extends StrongKeyNode {
volatile int value;
StrongKeyIntValueNode(int locator, Object key, Object value) {
super(locator, key);
this.value = ((Integer) value).intValue();
}
public final Object getValue() {
return Integer.valueOf(value);
}
public final void onReclamation() {}
public final void setValue(Object value) {
this.value = ((Integer) value).intValue();
}
}
static final class StrongKeyIntValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalStrongKeyIntValueNode(locator, key, value);
} else {
return new LinkedStrongKeyIntValueNode(locator, key, value, linkage);
}
}
}
abstract static class StrongKeyNode implements Node {
final Object key;
final int locator;
StrongKeyNode(int locator, Object key) {
this.locator = locator;
this.key = key;
}
public final Object get() {
return key;
}
public int getLocator() {
return locator;
}
}
abstract static class StrongKeySelfValueNode extends StrongKeyNode {
StrongKeySelfValueNode(int locator, Object key) {
super(locator, key);
}
public final Object getValue() {
return key;
}
public final void onReclamation() {}
public final void setValue(Object value) {}
}
static final class StrongKeySelfValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalStrongKeySelfValueNode(locator, key);
} else {
return new LinkedStrongKeySelfValueNode(locator, key, linkage);
}
}
}
abstract static class StrongKeySoftValueNode extends StrongKeyNode {
final CustomConcurrentHashMap cchm;
volatile EmbeddedSoftReference valueRef;
StrongKeySoftValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key);
this.cchm = cchm;
if (value != null) {
this.valueRef = new EmbeddedSoftReference(value, this);
}
}
public final Object getValue() {
EmbeddedSoftReference vr = valueRef;
return vr == null ? null : vr.get();
}
public final void onReclamation() {
cchm.removeIfReclaimed(this);
}
public final void setValue(Object value) {
if (value == null) {
valueRef = null;
} else {
valueRef = new EmbeddedSoftReference(value, this);
}
}
}
static final class StrongKeySoftValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalStrongKeySoftValueNode(locator, key, value, cchm);
} else {
return new LinkedStrongKeySoftValueNode(locator, key, value, cchm, linkage);
}
}
}
abstract static class StrongKeyStrongValueNode extends StrongKeyNode {
volatile Object value;
StrongKeyStrongValueNode(int locator, Object key, Object value) {
super(locator, key);
this.value = value;
}
public final Object getValue() {
return value;
}
public final void onReclamation() {}
public final void setValue(Object value) {
this.value = value;
}
}
static final class StrongKeyStrongValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalStrongKeyStrongValueNode(locator, key, value);
} else {
return new LinkedStrongKeyStrongValueNode(locator, key, value, linkage);
}
}
}
abstract static class StrongKeyWeakValueNode extends StrongKeyNode {
final CustomConcurrentHashMap cchm;
volatile EmbeddedWeakReference valueRef;
StrongKeyWeakValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key);
this.cchm = cchm;
if (value != null) {
this.valueRef = new EmbeddedWeakReference(value, this);
}
}
public final Object getValue() {
EmbeddedWeakReference vr = valueRef;
return vr == null ? null : vr.get();
}
public final void onReclamation() {
cchm.removeIfReclaimed(this);
}
public final void setValue(Object value) {
if (value == null) {
valueRef = null;
} else {
valueRef = new EmbeddedWeakReference(value, this);
}
}
}
static final class StrongKeyWeakValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalStrongKeyWeakValueNode(locator, key, value, cchm);
} else {
return new LinkedStrongKeyWeakValueNode(locator, key, value, cchm, linkage);
}
}
}
static final class TerminalIntKeyIntValueNode extends IntKeyIntValueNode {
TerminalIntKeyIntValueNode(int locator, Object key, Object value) {
super(locator, key, value);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalIntKeySelfValueNode extends IntKeySelfValueNode {
TerminalIntKeySelfValueNode(int locator, Object key) {
super(locator, key);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalIntKeySoftValueNode extends IntKeySoftValueNode {
TerminalIntKeySoftValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, value, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalIntKeyStrongValueNode extends IntKeyStrongValueNode {
TerminalIntKeyStrongValueNode(int locator, Object key, Object value) {
super(locator, key, value);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalIntKeyWeakValueNode extends IntKeyWeakValueNode {
TerminalIntKeyWeakValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, value, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalSoftKeyIntValueNode extends SoftKeyIntValueNode {
TerminalSoftKeyIntValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, value, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalSoftKeySelfValueNode extends SoftKeySelfValueNode {
TerminalSoftKeySelfValueNode(int locator, Object key, CustomConcurrentHashMap cchm) {
super(locator, key, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalSoftKeySoftValueNode extends SoftKeySoftValueNode {
TerminalSoftKeySoftValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, value, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalSoftKeyStrongValueNode extends SoftKeyStrongValueNode {
TerminalSoftKeyStrongValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, value, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalSoftKeyWeakValueNode extends SoftKeyWeakValueNode {
TerminalSoftKeyWeakValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, value, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalStrongKeyIntValueNode extends StrongKeyIntValueNode {
TerminalStrongKeyIntValueNode(int locator, Object key, Object value) {
super(locator, key, value);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalStrongKeySelfValueNode extends StrongKeySelfValueNode {
TerminalStrongKeySelfValueNode(int locator, Object key) {
super(locator, key);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalStrongKeySoftValueNode extends StrongKeySoftValueNode {
TerminalStrongKeySoftValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, value, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalStrongKeyStrongValueNode extends StrongKeyStrongValueNode {
TerminalStrongKeyStrongValueNode(int locator, Object key, Object value) {
super(locator, key, value);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalStrongKeyWeakValueNode extends StrongKeyWeakValueNode {
TerminalStrongKeyWeakValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, value, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalWeakKeyIntValueNode extends WeakKeyIntValueNode {
TerminalWeakKeyIntValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, value, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalWeakKeySelfValueNode extends WeakKeySelfValueNode {
TerminalWeakKeySelfValueNode(int locator, Object key, CustomConcurrentHashMap cchm) {
super(locator, key, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalWeakKeySoftValueNode extends WeakKeySoftValueNode {
TerminalWeakKeySoftValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, value, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalWeakKeyStrongValueNode extends WeakKeyStrongValueNode {
TerminalWeakKeyStrongValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, value, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
static final class TerminalWeakKeyWeakValueNode extends WeakKeyWeakValueNode {
TerminalWeakKeyWeakValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, value, cchm);
}
public Node getLinkage() {
return null;
}
public void setLinkage(Node linkage) {}
}
final class ValueIterator extends HashIterator implements Iterator<V> {
public V next() {
return super.nextValue();
}
}
final class Values extends AbstractCollection<V> {
public void clear() {
CustomConcurrentHashMap.this.clear();
}
public boolean contains(Object o) {
return CustomConcurrentHashMap.this.containsValue(o);
}
public boolean isEmpty() {
return CustomConcurrentHashMap.this.isEmpty();
}
public Iterator<V> iterator() {
return new ValueIterator();
}
public int size() {
return CustomConcurrentHashMap.this.size();
}
}
abstract static class WeakKeyIntValueNode extends WeakKeyNode {
volatile int value;
WeakKeyIntValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, cchm);
this.value = ((Integer) value).intValue();
}
public final Object getValue() {
return Integer.valueOf(value);
}
public final void setValue(Object value) {
this.value = ((Integer) value).intValue();
}
}
static final class WeakKeyIntValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalWeakKeyIntValueNode(locator, key, value, cchm);
} else {
return new LinkedWeakKeyIntValueNode(locator, key, value, cchm, linkage);
}
}
}
abstract static class WeakKeyNode extends WeakReference implements Node {
final CustomConcurrentHashMap cchm;
final int locator;
WeakKeyNode(int locator, Object key, CustomConcurrentHashMap cchm) {
super(key, getReclamationQueue());
this.locator = locator;
this.cchm = cchm;
}
public int getLocator() {
return locator;
}
public final void onReclamation() {
clear();
cchm.removeIfReclaimed(this);
}
}
// Temporary Unsafe mechanics for preliminary release
abstract static class WeakKeySelfValueNode extends WeakKeyNode {
WeakKeySelfValueNode(int locator, Object key, CustomConcurrentHashMap cchm) {
super(locator, key, cchm);
}
public final Object getValue() {
return get();
}
public final void setValue(Object value) {}
}
static final class WeakKeySelfValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalWeakKeySelfValueNode(locator, key, cchm);
} else {
return new LinkedWeakKeySelfValueNode(locator, key, cchm, linkage);
}
}
}
abstract static class WeakKeySoftValueNode extends WeakKeyNode {
volatile EmbeddedSoftReference valueRef;
WeakKeySoftValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, cchm);
if (value != null) {
this.valueRef = new EmbeddedSoftReference(value, this);
}
}
public final Object getValue() {
EmbeddedSoftReference vr = valueRef;
return vr == null ? null : vr.get();
}
public final void setValue(Object value) {
if (value == null) {
valueRef = null;
} else {
valueRef = new EmbeddedSoftReference(value, this);
}
}
}
static final class WeakKeySoftValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalWeakKeySoftValueNode(locator, key, value, cchm);
} else {
return new LinkedWeakKeySoftValueNode(locator, key, value, cchm, linkage);
}
}
}
abstract static class WeakKeyStrongValueNode extends WeakKeyNode {
volatile Object value;
WeakKeyStrongValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, cchm);
this.value = value;
}
public final Object getValue() {
return value;
}
public final void setValue(Object value) {
this.value = value;
}
}
static final class WeakKeyStrongValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalWeakKeyStrongValueNode(locator, key, value, cchm);
} else {
return new LinkedWeakKeyStrongValueNode(locator, key, value, cchm, linkage);
}
}
}
abstract static class WeakKeyWeakValueNode extends WeakKeyNode {
volatile EmbeddedWeakReference valueRef;
WeakKeyWeakValueNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm) {
super(locator, key, cchm);
if (value != null) {
this.valueRef = new EmbeddedWeakReference(value, this);
}
}
public final Object getValue() {
EmbeddedWeakReference vr = valueRef;
return vr == null ? null : vr.get();
}
public final void setValue(Object value) {
if (value == null) {
valueRef = null;
} else {
valueRef = new EmbeddedWeakReference(value, this);
}
}
}
static final class WeakKeyWeakValueNodeFactory implements NodeFactory, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
public Node newNode(int locator, Object key, Object value, CustomConcurrentHashMap cchm, Node linkage) {
if (linkage == null) {
return new TerminalWeakKeyWeakValueNode(locator, key, value, cchm);
} else {
return new LinkedWeakKeyWeakValueNode(locator, key, value, cchm, linkage);
}
}
}
final class WriteThroughEntry implements Map.Entry<K, V>, Serializable {
private static final long serialVersionUID = 7249069346764182397L;
final K key;
V value;
WriteThroughEntry(K key, V value) {
this.key = key;
this.value = value;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry)) {
return false;
}
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
return keyEquivalence.equal(key, e.getKey()) && valueEquivalence.equal(value, e.getValue());
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public int hashCode() {
return keyEquivalence.hash(key) ^ valueEquivalence.hash(value);
}
public V setValue(V value) {
if (value == null) {
throw new NullPointerException();
}
V v = this.value;
this.value = value;
CustomConcurrentHashMap.this.doPut(key, value, false);
return v;
}
}
}
// CHECKSTYLE:ON