[] segments = this.segments;
long sum = 0;
long check = 0;
int[] mc = new int[segments.length];
// Try a few times to get accurate count. On failure due to continuous
// async changes in table, resort to locking.
for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
check = 0;
sum = 0;
int mcsum = 0;
for (int i = 0; i < segments.length; ++i) {
sum += segments[i].count;
mcsum += mc[i] = segments[i].modCount;
}
if (mcsum != 0) {
for (int i = 0; i < segments.length; ++i) {
check += segments[i].count;
if (mc[i] != segments[i].modCount) {
check = -1; // force retry
break;
}
}
}
if (check == sum) {
break;
}
}
if (check != sum) { // Resort to locking all segments
sum = 0;
for (int i = 0; i < segments.length; ++i) {
segments[i].lock();
}
for (int i = 0; i < segments.length; ++i) {
sum += segments[i].count;
}
for (int i = 0; i < segments.length; ++i) {
segments[i].unlock();
}
}
if (sum > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
} else {
return (int) sum;
}
}
/**
* Returns the value to which the specified key is mapped, or {@code null} if this map contains no mapping for the
* key.
*
* More formally, if this map contains a mapping from a key {@code k} to a value {@code v} such that
* {@code key.equals(k)}, then this method returns {@code v}; otherwise it returns {@code null}. (There can be at
* most one such mapping.)
*
* @throws NullPointerException if the specified key is null
*/
@Override
public V get(Object key) {
int hash = hashOf(key);
return segmentFor(hash).get(key, hash);
}
/**
* Tests if the specified object is a key in this table.
*
* @param key possible key
* @return true if and only if the specified object is a key in this table, as determined by the
* equals method; false otherwise.
* @throws NullPointerException if the specified key is null
*/
@Override
public boolean containsKey(Object key) {
int hash = hashOf(key);
return segmentFor(hash).containsKey(key, hash);
}
/**
* Returns true if this map maps one or more keys to the specified value. Note: This method requires a full
* internal traversal of the hash table, and so is much slower than method containsKey.
*
* @param value value whose presence in this map is to be tested
* @return true if this map maps one or more keys to the specified value
* @throws NullPointerException if the specified value is null
*/
@Override
public boolean containsValue(Object value) {
if (value == null) {
throw new NullPointerException();
}
// See explanation of modCount use above
final Segment[] segments = this.segments;
int[] mc = new int[segments.length];
// Try a few times without locking
for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
int mcsum = 0;
for (int i = 0; i < segments.length; ++i) {
mcsum += mc[i] = segments[i].modCount;
if (segments[i].containsValue(value)) {
return true;
}
}
boolean cleanSweep = true;
if (mcsum != 0) {
for (int i = 0; i < segments.length; ++i) {
if (mc[i] != segments[i].modCount) {
cleanSweep = false;
break;
}
}
}
if (cleanSweep) {
return false;
}
}
// Resort to locking all segments
for (int i = 0; i < segments.length; ++i) {
segments[i].lock();
}
boolean found = false;
try {
for (int i = 0; i < segments.length; ++i) {
if (segments[i].containsValue(value)) {
found = true;
break;
}
}
} finally {
for (int i = 0; i < segments.length; ++i) {
segments[i].unlock();
}
}
return found;
}
/**
* Legacy method testing if some key maps into the specified value in this table. This method is identical in
* functionality to {@link #containsValue}, and exists solely to ensure full compatibility with class
* {@link Hashtable}, which supported this method prior to introduction of the Java Collections framework.
*
* @param value a value to search for
* @return true if and only if some key maps to the value argument in this table as determined by
* the equals method; false otherwise
* @throws NullPointerException if the specified value is null
*/
public boolean contains(Object value) {
return containsValue(value);
}
/**
* Maps the specified key to the specified value in this table. Neither the key nor the value can be null.
*
* The value can be retrieved by calling the get method with a key that is equal to the original key.
*
* @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 key, or null if there was no mapping for
* key
* @throws NullPointerException if the specified key or value is null
*/
@Override
public V put(K key, V value) {
if (value == null) {
throw new NullPointerException();
}
int hash = hashOf(key);
return segmentFor(hash).put(key, hash, value, false);
}
/**
* {@inheritDoc}
*
* @return the previous value associated with the specified key, or 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) {
if (value == null) {
throw new NullPointerException();
}
int hash = hashOf(key);
return segmentFor(hash).put(key, hash, value, true);
}
/**
* 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
*/
@Override
public void putAll(Map extends K, ? extends V> m) {
for (Map.Entry extends K, ? extends V> e : m.entrySet()) {
put(e.getKey(), e.getValue());
}
}
/**
* Removes the key (and its corresponding value) from this map. This method does nothing if the key is not in the
* map.
*
* @param key the key that needs to be removed
* @return the previous value associated with key, or null if there was no mapping for
* key
* @throws NullPointerException if the specified key is null
*/
@Override
public V remove(Object key) {
int hash = hashOf(key);
return segmentFor(hash).remove(key, hash, null, false);
}
/**
* {@inheritDoc}
*
* @throws NullPointerException if the specified key is null
*/
public boolean remove(Object key, Object value) {
int hash = hashOf(key);
if (value == null) {
return false;
}
return segmentFor(hash).remove(key, hash, value, false) != null;
}
/**
* {@inheritDoc}
*
* @throws NullPointerException if any of the arguments are null
*/
public boolean replace(K key, V oldValue, V newValue) {
if (oldValue == null || newValue == null) {
throw new NullPointerException();
}
int hash = hashOf(key);
return segmentFor(hash).replace(key, hash, oldValue, newValue);
}
/**
* {@inheritDoc}
*
* @return the previous value associated with the specified key, or null if there was no mapping for the
* key
* @throws NullPointerException if the specified key or value is null
*/
public V replace(K key, V value) {
if (value == null) {
throw new NullPointerException();
}
int hash = hashOf(key);
return segmentFor(hash).replace(key, hash, value);
}
/**
* Removes all of the mappings from this map.
*/
@Override
public void clear() {
for (int i = 0; i < segments.length; ++i) {
segments[i].clear();
}
}
/**
* 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 Iterator.remove, Set.remove, removeAll,
* retainAll, and clear operations. It does not support the add or addAll
* operations.
*
* The view's 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.
*/
@Override
public Set keySet() {
Set ks = keySet;
return ks != null ? ks : (keySet = new KeySet());
}
/**
* 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 Iterator.remove,
* Collection.remove, removeAll, retainAll, and clear operations. It does not
* support the add or addAll operations.
*
* The view's 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.
*/
@Override
public Collection values() {
Collection vs = values;
return vs != null ? vs : (values = new Values());
}
/**
* 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 Iterator.remove, Set.remove, removeAll,
* retainAll, and clear operations. It does not support the add or addAll
* operations.
*
* The view's 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.
*/
@Override
public Set> entrySet() {
Set> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
/**
* Returns an enumeration of the keys in this table.
*
* @return an enumeration of the keys in this table
* @see #keySet()
*/
public Enumeration keys() {
return new KeyIterator();
}
/**
* Returns an enumeration of the values in this table.
*
* @return an enumeration of the values in this table
* @see #values()
*/
public Enumeration elements() {
return new ValueIterator();
}
/* ---------------- Iterator Support -------------- */
abstract class HashIterator {
int nextSegmentIndex;
int nextTableIndex;
HashEntry[] currentTable;
HashEntry nextEntry;
HashEntry lastReturned;
K currentKey; // Strong reference to weak key (prevents gc)
HashIterator(){
nextSegmentIndex = segments.length - 1;
nextTableIndex = -1;
advance();
}
public boolean hasMoreElements() {
return hasNext();
}
final void advance() {
if (nextEntry != null && (nextEntry = nextEntry.next) != null) {
return;
}
while (nextTableIndex >= 0) {
if ((nextEntry = currentTable[nextTableIndex--]) != null) {
return;
}
}
while (nextSegmentIndex >= 0) {
Segment seg = segments[nextSegmentIndex--];
if (seg.count != 0) {
currentTable = seg.table;
for (int j = currentTable.length - 1; j >= 0; --j) {
if ((nextEntry = currentTable[j]) != null) {
nextTableIndex = j - 1;
return;
}
}
}
}
}
public boolean hasNext() {
while (nextEntry != null) {
if (nextEntry.key() != null) {
return true;
}
advance();
}
return false;
}
HashEntry nextEntry() {
do {
if (nextEntry == null) {
throw new NoSuchElementException();
}
lastReturned = nextEntry;
currentKey = lastReturned.key();
advance();
} while (currentKey == null); // Skip GC'd keys
return lastReturned;
}
public void remove() {
if (lastReturned == null) {
throw new IllegalStateException();
}
ConcurrentIdentityHashMap.this.remove(currentKey);
lastReturned = null;
}
}
final class KeyIterator extends HashIterator implements Iterator, Enumeration {
public K next() {
return super.nextEntry().key();
}
public K nextElement() {
return super.nextEntry().key();
}
}
final class ValueIterator extends HashIterator implements Iterator, Enumeration {
public V next() {
return super.nextEntry().value();
}
public V nextElement() {
return super.nextEntry().value();
}
}
/*
* This class is needed for JDK5 compatibility.
*/
static class SimpleEntry implements Entry {
private final K key;
private V value;
public SimpleEntry(K key, V value){
this.key = key;
this.value = value;
}
public SimpleEntry(Entry extends K, ? extends V> entry){
this.key = entry.getKey();
this.value = entry.getValue();
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Map.Entry, ?>)) {
return false;
}
@SuppressWarnings("unchecked")
Map.Entry e = (Map.Entry) o;
return eq(key, e.getKey()) && eq(value, e.getValue());
}
@Override
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
}
@Override
public String toString() {
return key + "=" + value;
}
private static boolean eq(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
}
/**
* Custom Entry class used by EntryIterator.next(), that relays setValue changes to the underlying map.
*/
final class WriteThroughEntry extends SimpleEntry {
WriteThroughEntry(K k, V v){
super(k, v);
}
/**
* Set our entry's value and write through to the map. The value to return is somewhat arbitrary here. Since a
* WriteThroughEntry does not necessarily track asynchronous changes, the most recent "previous" value could be
* different from what we return (or could even have been removed in which case the put will re-establish). We
* do not and can not guarantee more.
*/
@Override
public V setValue(V value) {
if (value == null) {
throw new NullPointerException();
}
V v = super.setValue(value);
ConcurrentIdentityHashMap.this.put(getKey(), value);
return v;
}
}
final class EntryIterator extends HashIterator implements Iterator> {
public Map.Entry next() {
HashEntry e = super.nextEntry();
return new WriteThroughEntry(e.key(), e.value());
}
}
final class KeySet extends AbstractSet {
@Override
public Iterator iterator() {
return new KeyIterator();
}
@Override
public int size() {
return ConcurrentIdentityHashMap.this.size();
}
@Override
public boolean isEmpty() {
return ConcurrentIdentityHashMap.this.isEmpty();
}
@Override
public boolean contains(Object o) {
return ConcurrentIdentityHashMap.this.containsKey(o);
}
@Override
public boolean remove(Object o) {
return ConcurrentIdentityHashMap.this.remove(o) != null;
}
@Override
public void clear() {
ConcurrentIdentityHashMap.this.clear();
}
}
final class Values extends AbstractCollection {
@Override
public Iterator iterator() {
return new ValueIterator();
}
@Override
public int size() {
return ConcurrentIdentityHashMap.this.size();
}
@Override
public boolean isEmpty() {
return ConcurrentIdentityHashMap.this.isEmpty();
}
@Override
public boolean contains(Object o) {
return ConcurrentIdentityHashMap.this.containsValue(o);
}
@Override
public void clear() {
ConcurrentIdentityHashMap.this.clear();
}
}
final class EntrySet extends AbstractSet> {
@Override
public Iterator> iterator() {
return new EntryIterator();
}
@Override
public boolean contains(Object o) {
if (!(o instanceof Map.Entry, ?>)) {
return false;
}
Map.Entry, ?> e = (Map.Entry, ?>) o;
V v = ConcurrentIdentityHashMap.this.get(e.getKey());
return v != null && v.equals(e.getValue());
}
@Override
public boolean remove(Object o) {
if (!(o instanceof Map.Entry, ?>)) {
return false;
}
Map.Entry, ?> e = (Map.Entry, ?>) o;
return ConcurrentIdentityHashMap.this.remove(e.getKey(), e.getValue());
}
@Override
public int size() {
return ConcurrentIdentityHashMap.this.size();
}
@Override
public boolean isEmpty() {
return ConcurrentIdentityHashMap.this.isEmpty();
}
@Override
public void clear() {
ConcurrentIdentityHashMap.this.clear();
}
}
}