Ahh horrible bugs in processing soft/weak reference queue in concurrent collections

This commit is contained in:
Alexey Kudravtsev
2012-08-24 11:09:23 +04:00
parent 4405d5d031
commit b78c7d414f
9 changed files with 765 additions and 1067 deletions
@@ -0,0 +1,388 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* 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.
*/
/*
* Created by IntelliJ IDEA.
* User: Alexey
* Date: 18.12.2006
* Time: 20:18:31
*/
package com.intellij.util.containers;
import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.ReferenceQueue;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
/**
* Fully copied from java.util.WeakHashMap except "get" method optimization.
*/
public abstract class ConcurrentRefHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V> {
protected interface Key<K, V>{
K get();
V getValue();
// MUST work even with gced references for the code in processQueue to work
boolean equals(Object o);
int hashCode();
}
protected abstract Key<K, V> createKey(@NotNull K key, V value);
private static class HardKey<K,V> implements Key<K,V> {
private K myKey;
private int myHash;
private final V value;
public HardKey(K key, V value) {
this.value = value;
setKey(key);
}
private void setKey(K key) {
myKey = key;
myHash = key != null ? key.hashCode() : 0;
}
@Override
public K get() {
return myKey;
}
@Override
public V getValue() {
return value;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Object t = get();
Object u = ((Key)o).get();
if (t == null || u == null) return false;
if (t == u) return true;
return t.equals(u);
}
public int hashCode() {
return myHash;
}
}
private final ConcurrentMap<Key<K, V>, V> myMap;
private static final Key NULL_KEY = new Key() {
@Override
public Object get() { return null; }
@Override
public Object getValue() { return null; }
};
protected final ReferenceQueue<K> myReferenceQueue = new ReferenceQueue<K>();
private void processQueue() {
Key<K,V> wk;
while((wk = (Key)myReferenceQueue.poll()) != null){
V value = wk.getValue();
myMap.remove(wk, value);
}
}
public ConcurrentRefHashMap(int initialCapacity, float loadFactor) {
myMap = new ConcurrentHashMap<Key<K, V>, V>(initialCapacity, loadFactor, 4);
}
public ConcurrentRefHashMap(int initialCapacity) {
myMap = new ConcurrentHashMap<Key<K, V>, V>(initialCapacity);
}
public ConcurrentRefHashMap() {
myMap = new ConcurrentHashMap<Key<K, V>, V>();
}
public ConcurrentRefHashMap(int initialCapacity, float loadFactor, int concurrencyLevel, TObjectHashingStrategy<Key<K, V>> hashingStrategy) {
myMap = new ConcurrentHashMap<Key<K, V>, V>(initialCapacity, loadFactor, concurrencyLevel, hashingStrategy);
}
public ConcurrentRefHashMap(Map<? extends K, ? extends V> t) {
this(Math.max(2 * t.size(), 11), 0.75f);
putAll(t);
}
public ConcurrentRefHashMap(final TObjectHashingStrategy<K> hashingStrategy) {
myMap = new ConcurrentHashMap<Key<K, V>, V>(new TObjectHashingStrategy<Key<K, V>>() {
@Override
public int computeHashCode(final Key<K, V> object) {
return hashingStrategy.computeHashCode(object.get());
}
@Override
public boolean equals(final Key<K, V> o1, final Key<K, V> o2) {
return hashingStrategy.equals(o1.get(), o2.get());
}
} );
}
@Override
public int size() {
return entrySet().size();
}
@Override
public boolean isEmpty() {
return entrySet().isEmpty();
}
@Override
public boolean containsKey(Object key) {
// optimization:
if (key == null){
return myMap.containsKey(NULL_KEY);
}
else{
HardKey<K,V> hardKey = createHardKey((K)key);
boolean result = myMap.containsKey(hardKey);
releaseHardKey(hardKey);
return result;
}
//return myMap.containsKey(WeakKey.create(key));
}
private static final ThreadLocal<HardKey> myHardKey = new ThreadLocal<HardKey>(){
@Override
protected HardKey initialValue() {
return new HardKey(null, null);
}
};
private static <K,V> HardKey<K,V> createHardKey(K key) {
HardKey hardKey = myHardKey.get();
hardKey.setKey(key);
return hardKey;
}
private static void releaseHardKey(HardKey key) {
key.setKey(null);
}
@Override
public V get(Object key) {
//return myMap.get(WeakKey.create(key));
// optimization:
if (key == null){
return myMap.get(NULL_KEY);
}
else{
HardKey<K,V> hardKey = createHardKey((K)key);
V result = myMap.get(hardKey);
releaseHardKey(hardKey);
return result;
}
}
@Override
public V put(K key, V value) {
processQueue();
Key<K, V> weakKey = key == null ? NULL_KEY : createKey(key, value);
return myMap.put(weakKey, value);
}
@Override
public V remove(Object key) {
processQueue();
// optimization:
if (key == null){
return myMap.remove(NULL_KEY);
}
else{
HardKey hardKey = createHardKey(key);
V result = myMap.remove(hardKey);
releaseHardKey(hardKey);
return result;
}
}
@Override
public void clear() {
processQueue();
myMap.clear();
}
private static class Entry<K,V> implements Map.Entry<K,V> {
private final Map.Entry<?,V> ent;
private final K key; /* Strong reference to key, so that the GC
will leave it alone as long as this Entry
exists */
Entry(Map.Entry<?,V> ent, K key) {
this.ent = ent;
this.key = key;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return ent.getValue();
}
@Override
public V setValue(V value) {
return ent.setValue(value);
}
private static boolean valEquals(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry)) return false;
Map.Entry e = (Map.Entry)o;
return valEquals(key, e.getKey()) && valEquals(getValue(), e.getValue());
}
public int hashCode() {
Object v;
return (key == null ? 0 : key.hashCode()) ^ ((v = getValue()) == null ? 0 : v.hashCode());
}
}
/* Internal class for entry sets */
private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
Set<Map.Entry<Key<K, V>,V>> hashEntrySet = myMap.entrySet();
@Override
public Iterator<Map.Entry<K,V>> iterator() {
return new Iterator<Map.Entry<K,V>>() {
Iterator<Map.Entry<Key<K, V>,V>> hashIterator = hashEntrySet.iterator();
Entry<K,V> next = null;
@Override
public boolean hasNext() {
while(hashIterator.hasNext()){
Map.Entry<Key<K, V>, V> ent = hashIterator.next();
Key<K, V> wk = ent.getKey();
K k = null;
if (wk != null && (k = wk.get()) == null){
/* Weak key has been cleared by GC */
continue;
}
next = new Entry<K,V>(ent, k);
return true;
}
return false;
}
@Override
public Map.Entry<K, V> next() {
if (next == null && !hasNext()) {
throw new NoSuchElementException();
}
Entry<K,V> e = next;
next = null;
return e;
}
@Override
public void remove() {
hashIterator.remove();
}
};
}
@Override
public boolean isEmpty() {
return !iterator().hasNext();
}
@Override
public int size() {
int j = 0;
for(Iterator i = iterator(); i.hasNext(); i.next()) j++;
return j;
}
@Override
public boolean remove(Object o) {
processQueue();
if (!(o instanceof Map.Entry)) return false;
Map.Entry e = (Map.Entry)o;
Object ev = e.getValue();
HardKey key = createHardKey(o);
V hv = myMap.get(key);
boolean toRemove = hv == null ? ev == null && myMap.containsKey(key) : hv.equals(ev);
if (toRemove){
myMap.remove(key);
}
releaseHardKey(key);
return toRemove;
}
public int hashCode() {
int h = 0;
for (Object aHashEntrySet : hashEntrySet) {
Map.Entry ent = (Map.Entry)aHashEntrySet;
Key wk = (Key)ent.getKey();
if (wk == null) continue;
Object v;
h += wk.hashCode() ^ ((v = ent.getValue()) == null ? 0 : v.hashCode());
}
return h;
}
}
private Set<Map.Entry<K,V>> entrySet = null;
@Override
public Set<Map.Entry<K,V>> entrySet() {
if (entrySet == null) entrySet = new EntrySet();
return entrySet;
}
@Override
public V putIfAbsent(@NotNull final K key, final V value) {
processQueue();
return myMap.putIfAbsent(createKey(key, value), value);
}
@Override
public boolean remove(@NotNull final Object key, final Object value) {
processQueue();
return myMap.remove(createKey((K)key, (V)value), value);
}
@Override
public boolean replace(@NotNull final K key, @NotNull final V oldValue, @NotNull final V newValue) {
processQueue();
return myMap.replace(createKey(key, oldValue), oldValue,newValue);
}
@Override
public V replace(@NotNull final K key, @NotNull final V value) {
processQueue();
return myMap.replace(createKey(key, value), value);
}
}
@@ -0,0 +1,211 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.containers;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.ReferenceQueue;
import java.util.*;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public abstract class ConcurrentRefValueHashMap<K,V> implements ConcurrentMap<K,V> {
private final ConcurrentHashMap<K,MyReference<K, V>> myMap;
protected final ReferenceQueue<V> myQueue = new ReferenceQueue<V>();
public ConcurrentRefValueHashMap(final Map<K, V> map) {
this();
putAll(map);
}
public ConcurrentRefValueHashMap() {
myMap = new ConcurrentHashMap<K, MyReference<K, V>>();
}
public ConcurrentRefValueHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
myMap = new ConcurrentHashMap<K, MyReference<K, V>>(initialCapacity, loadFactor, concurrencyLevel);
}
protected interface MyReference<K, V> {
K getKey();
V get();
}
private void processQueue() {
while(true){
MyReference<K, V> ref = (MyReference<K, V>)myQueue.poll();
if (ref == null) break;
myMap.remove(ref.getKey(), ref);
}
}
@Override
public V get(@NotNull Object key) {
MyReference<K, V> ref = myMap.get(key);
if (ref == null) return null;
return ref.get();
}
@Override
public V put(@NotNull K key, @NotNull V value) {
processQueue();
MyReference<K, V> oldRef = myMap.put(key, createRef(key, value));
return oldRef != null ? oldRef.get() : null;
}
protected abstract MyReference<K, V> createRef(K key, V value);
@Override
public V putIfAbsent(@NotNull K key, V value) {
MyReference<K, V> newRef = createRef(key, value);
while (true) {
processQueue();
MyReference<K, V> oldRef = myMap.putIfAbsent(key, newRef);
if (oldRef == null) return null;
final V oldVal = oldRef.get();
if (oldVal == null) {
if (myMap.replace(key, oldRef, newRef)) return null;
}
else {
return oldVal;
}
}
}
@Override
public boolean remove(@NotNull final Object key, final Object value) {
processQueue();
return myMap.remove(key, createRef((K)key, (V)value));
}
@Override
public boolean replace(@NotNull final K key, @NotNull final V oldValue, @NotNull final V newValue) {
processQueue();
return myMap.replace(key, createRef(key, oldValue), createRef(key, newValue));
}
@Override
public V replace(@NotNull final K key, @NotNull final V value) {
processQueue();
MyReference<K, V> ref = myMap.replace(key, createRef(key, value));
return ref == null ? null : ref.get();
}
@Override
public V remove(Object key) {
processQueue();
MyReference<K, V> ref = myMap.remove(key);
return ref != null ? ref.get() : null;
}
@Override
public void putAll(Map<? extends K, ? extends V> t) {
processQueue();
for (K k : t.keySet()) {
V v = t.get(k);
if (v != null) {
put(k, v);
}
}
}
@Override
public void clear() {
myMap.clear();
processQueue();
}
@Override
public int size() {
return myMap.size(); //?
}
@Override
public boolean isEmpty() {
return myMap.isEmpty(); //?
}
@Override
public boolean containsKey(Object key) {
return get(key) != null;
}
@Override
public boolean containsValue(Object value) {
throw new RuntimeException("method not implemented");
}
@Override
public Set<K> keySet() {
return myMap.keySet();
}
@Override
public Collection<V> values() {
List<V> result = new ArrayList<V>();
final Collection<MyReference<K, V>> refs = myMap.values();
for (MyReference<K, V> ref : refs) {
final V value = ref.get();
if (value != null) {
result.add(value);
}
}
return result;
}
@Override
public Set<Entry<K, V>> entrySet() {
final Set<K> keys = keySet();
Set<Entry<K, V>> entries = new HashSet<Entry<K, V>>();
for (final K key : keys) {
final V value = get(key);
if (value != null) {
entries.add(new Entry<K, V>() {
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
throw new UnsupportedOperationException("setValue is not implemented");
}
});
}
}
return entries;
}
@Override
public String toString() {
@NonNls String s = "map size:" + size() + " [";
for (K k : myMap.keySet()) {
Object v = get(k);
s += "'"+k + "': '" +v+"', ";
}
s += "] ";
return s;
}
}
@@ -22,6 +22,7 @@ import org.jetbrains.annotations.NotNull;
import java.lang.ref.ReferenceQueue;
import java.util.Iterator;
import java.util.NoSuchElementException;
abstract class ConcurrentRefValueIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
private final StripedLockIntObjectConcurrentHashMap<IntReference<V>> myMap = new StripedLockIntObjectConcurrentHashMap<IntReference<V>>();
@@ -34,14 +35,15 @@ abstract class ConcurrentRefValueIntObjectHashMap<V> implements ConcurrentIntObj
V get();
}
void processQueue() {
while(true){
IntReference ref = (IntReference)myQueue.poll();
private void processQueue() {
while (true) {
@SuppressWarnings("unchecked")
IntReference<V> ref = (IntReference)myQueue.poll();
if (ref == null) {
return;
}
int key = ref.getKey();
myMap.remove(key);
myMap.remove(key, ref);
}
}
@@ -49,9 +51,19 @@ abstract class ConcurrentRefValueIntObjectHashMap<V> implements ConcurrentIntObj
@Override
public V cacheOrGet(int key, @NotNull V value) {
processQueue();
IntReference<V> ref = myMap.putIfAbsent(key, createReference(key, value, myQueue));
V v = ref == null ? null : ref.get();
return v == null ? value : v;
IntReference<V> newRef = createReference(key, value, myQueue);
while (true) {
IntReference<V> ref = myMap.putIfAbsent(key, newRef);
if (ref == null) return value; // there were no previous value
V old = ref.get();
if (old != null) return old;
// old value has been gced; need to overwrite
boolean replaced = myMap.replace(key, ref, newRef);
if (replaced) {
return value;
}
}
}
@Override
@@ -113,6 +125,7 @@ abstract class ConcurrentRefValueIntObjectHashMap<V> implements ConcurrentIntObj
@Override
public StripedLockIntObjectConcurrentHashMap.IntEntry<V> next() {
if (!hasNext()) throw new NoSuchElementException();
StripedLockIntObjectConcurrentHashMap.IntEntry<V> result = next;
next = nextAliveEntry();
return result;
@@ -27,356 +27,67 @@ import org.jetbrains.annotations.NotNull;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.Map;
/**
* Fully copied from java.util.SoftHashMap except "get" method optimization.
*/
public final class ConcurrentSoftHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V> {
private interface Key<K>{
K get();
}
private static class SoftKey<K> extends SoftReference<K> implements Key<K> {
public final class ConcurrentSoftHashMap<K,V> extends ConcurrentRefHashMap<K,V> {
private static class SoftKey<K, V> extends SoftReference<K> implements ConcurrentRefHashMap.Key<K, V> {
private final int myHash; /* Hashcode of key, stored here since the key may be tossed by the GC */
private final V value;
private SoftKey(K k) {
super(k);
myHash = k.hashCode();
}
public static<K> SoftKey<K> create(K k) {
return k == null ? null : new SoftKey<K>(k);
}
private SoftKey(K k, ReferenceQueue<? super K> q) {
private SoftKey(@NotNull K k, V v, ReferenceQueue<K> q) {
super(k, q);
value = v;
myHash = k.hashCode();
}
private static <K> SoftKey<K> create(K k, ReferenceQueue<? super K> q) {
return k == null ? null : new SoftKey<K>(k, q);
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Object t = get();
Object u = ((Key)o).get();
if (t == null || u == null) return false;
return t == u || t.equals(u);
}
public int hashCode() {
return myHash;
}
}
private static class HardKey<K> implements Key<K> {
private final K myObject;
private final int myHash;
public HardKey(K object) {
myObject = object;
myHash = object.hashCode();
}
@Override
public K get() {
return myObject;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Object t = get();
Object u = ((Key)o).get();
if (t == null || u == null) return false;
return t == u || t.equals(u);
}
public int hashCode() {
return myHash;
}
}
private final ConcurrentMap<Key<K>, V> myMap;
private static final Key<?> NULL_KEY = new HardKey<Object>("");
private final ReferenceQueue<K> myReferenceQueue = new ReferenceQueue<K>();
private void processQueue() {
SoftKey wk;
while((wk = (SoftKey)myReferenceQueue.poll()) != null){
myMap.remove(wk);
}
}
public ConcurrentSoftHashMap(int initialCapacity, float loadFactor) {
myMap = new ConcurrentHashMap<Key<K>, V>(initialCapacity, loadFactor, 4);
}
public ConcurrentSoftHashMap(int initialCapacity) {
myMap = new ConcurrentHashMap<Key<K>, V>(initialCapacity);
}
public ConcurrentSoftHashMap() {
myMap = new ConcurrentHashMap<Key<K>, V>();
}
public ConcurrentSoftHashMap(@NotNull Map<? extends K, ? extends V> t) {
this(Math.max(2 * t.size(), 11), 0.75f);
putAll(t);
}
public ConcurrentSoftHashMap(final TObjectHashingStrategy<K> hashingStrategy) {
myMap = new ConcurrentHashMap<Key<K>, V>(new TObjectHashingStrategy<Key<K>>() {
@Override
public int computeHashCode(final Key<K> object) {
return hashingStrategy.computeHashCode(object.get());
}
@Override
public boolean equals(final Key<K> o1, final Key<K> o2) {
return hashingStrategy.equals(o1.get(), o2.get());
}
} );
}
@Override
public int size() {
return entrySet().size();
}
@Override
public boolean isEmpty() {
return entrySet().isEmpty();
}
@Override
public boolean containsKey(Object key) {
// optimization:
if (key == null){
return myMap.containsKey(NULL_KEY);
}
else{
HardKey<Object> hardKey = new HardKey<Object>(key);
return myMap.containsKey(hardKey);
}
//return myMap.containsKey(SoftKey.create(key));
}
@Override
public V get(Object key) {
//return myMap.get(SoftKey.create(key));
// optimization:
if (key == null){
return myMap.get(NULL_KEY);
}
else{
HardKey<Object> hardKey = new HardKey<Object>(key);
V result = myMap.get(hardKey);
return (V)result;
}
}
@Override
public V put(K key, V value) {
processQueue();
SoftKey<K> softKey = SoftKey.create(key, myReferenceQueue);
Key<K> sk = softKey == null ? (Key<K>)NULL_KEY : softKey;
return myMap.put(sk, value);
}
@Override
public V remove(Object key) {
processQueue();
// optimization:
if (key == null){
return myMap.remove(NULL_KEY);
}
else{
HardKey<Object> hardKey = new HardKey<Object>(key);
return myMap.remove(hardKey);
}
//return myMap.remove(SoftKey.create(key));
}
@Override
public void clear() {
processQueue();
myMap.clear();
}
private static class Entry<K,V> implements Map.Entry<K,V> {
private final Map.Entry<K,V> ent;
private final K key; /* Strong reference to key, so that the GC
will leave it alone as long as this Entry
exists */
Entry(Map.Entry<K,V> ent, K key) {
this.ent = ent;
this.key = key;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return ent.getValue();
}
@Override
public V setValue(V value) {
return ent.setValue(value);
}
private static boolean valEquals(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
return value;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry)) return false;
Map.Entry e = (Map.Entry)o;
return valEquals(key, e.getKey())
&& valEquals(getValue(), e.getValue());
if (this == o) return true;
if (!(o instanceof ConcurrentRefHashMap.Key)) return false;
Object t = get();
Object u = ((ConcurrentRefHashMap.Key)o).get();
if (t == null || u == null) return false;
if (t == u) return true;
return t.equals(u);
}
public int hashCode() {
Object v;
return (key == null ? 0 : key.hashCode())
^ ((v = getValue()) == null ? 0 : v.hashCode());
return myHash;
}
}
/* Internal class for entry sets */
private class EntrySet extends AbstractSet {
Set hashEntrySet = myMap.entrySet();
@Override
public Iterator iterator() {
return new Iterator() {
Iterator hashIterator = hashEntrySet.iterator();
Entry next = null;
@Override
public boolean hasNext() {
while(hashIterator.hasNext()){
Map.Entry ent = (Map.Entry)hashIterator.next();
SoftKey wk = (SoftKey)ent.getKey();
Object k = null;
if (wk != null && (k = wk.get()) == null){
/* Soft key has been cleared by GC */
continue;
}
next = new Entry(ent, k);
return true;
}
return false;
}
@Override
public Object next() {
if (next == null && !hasNext())
throw new NoSuchElementException();
Entry e = next;
next = null;
return e;
}
@Override
public void remove() {
hashIterator.remove();
}
};
}
@Override
public boolean isEmpty() {
return !iterator().hasNext();
}
@Override
public int size() {
int j = 0;
for(Iterator i = iterator(); i.hasNext(); i.next()) j++;
return j;
}
@Override
public boolean remove(Object o) {
processQueue();
if (!(o instanceof Map.Entry)) return false;
Map.Entry<K,V> e = (Map.Entry)o;
V ev = e.getValue();
// optimization:
HardKey<K> key = new HardKey<K>(e.getKey());
//SoftKey key = SoftKey.create(e.getKey());
V hv = myMap.get(key);
boolean toRemove = hv == null ? ev == null && myMap.containsKey(key) : hv.equals(ev);
if (toRemove){
myMap.remove(key);
}
return toRemove;
}
public int hashCode() {
int h = 0;
for (Object aHashEntrySet : hashEntrySet) {
Map.Entry ent = (Map.Entry)aHashEntrySet;
SoftKey wk = (SoftKey)ent.getKey();
if (wk == null) {
continue;
}
Object v;
h += wk.hashCode()
^ ((v = ent.getValue()) == null ? 0 : v.hashCode());
}
return h;
}
}
private Set<Map.Entry<K,V>> entrySet = null;
@Override
public Set<Map.Entry<K,V>> entrySet() {
if (entrySet == null) entrySet = new EntrySet();
return entrySet;
}
@Override
public V putIfAbsent(@NotNull final K key, final V value) {
processQueue();
return myMap.putIfAbsent(SoftKey.create(key, myReferenceQueue), value);
protected ConcurrentRefHashMap.Key<K, V> createKey(@NotNull K key, V value) {
return new SoftKey<K, V>(key, value, myReferenceQueue);
}
@Override
public boolean remove(@NotNull final Object key, final Object value) {
processQueue();
return myMap.remove(SoftKey.create((K)key, myReferenceQueue), value);
public ConcurrentSoftHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
@Override
public boolean replace(@NotNull final K key, @NotNull final V oldValue, @NotNull final V newValue) {
processQueue();
return myMap.replace(SoftKey.create(key, myReferenceQueue), oldValue,newValue);
public ConcurrentSoftHashMap(int initialCapacity) {
super(initialCapacity);
}
@Override
public V replace(@NotNull final K key, @NotNull final V value) {
processQueue();
return myMap.replace(SoftKey.create(key, myReferenceQueue), value);
public ConcurrentSoftHashMap() {
}
public ConcurrentSoftHashMap(int initialCapacity,
float loadFactor,
int concurrencyLevel,
TObjectHashingStrategy<Key<K, V>> hashingStrategy) {
super(initialCapacity, loadFactor, concurrencyLevel, hashingStrategy);
}
public ConcurrentSoftHashMap(Map<? extends K, ? extends V> t) {
super(t);
}
public ConcurrentSoftHashMap(TObjectHashingStrategy<K> hashingStrategy) {
super(hashingStrategy);
}
}
@@ -16,209 +16,54 @@
package com.intellij.util.containers;
import com.intellij.reference.SoftReference;
import org.jetbrains.annotations.NotNull;
import com.intellij.openapi.util.Comparing;
import java.lang.ref.ReferenceQueue;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.lang.ref.SoftReference;
import java.util.Map;
public final class ConcurrentSoftValueHashMap<K,V> implements ConcurrentMap<K,V> {
private final ConcurrentMap<K,MyReference<K,V>> myMap = new ConcurrentHashMap<K, MyReference<K,V>>();
private final ReferenceQueue<MyReference<K,V>> myQueue = new ReferenceQueue<MyReference<K,V>>();
private static class MyReference<K,V> extends SoftReference<V> {
private final K key;
private MyReference(K key, V referent, ReferenceQueue q) {
super(referent, (ReferenceQueue<? super V>)q);
this.key = key;
}
public final class ConcurrentSoftValueHashMap<K,V> extends ConcurrentRefValueHashMap<K,V> {
public ConcurrentSoftValueHashMap(Map<K, V> map) {
super(map);
}
public ConcurrentSoftValueHashMap() {
super();
}
private void processQueue() {
while(true){
MyReference<K,V> ref = (MyReference<K,V>)myQueue.poll();
if (ref == null) {
return;
}
if (myMap.get(ref.key) == ref){
myMap.remove(ref.key);
}
public ConcurrentSoftValueHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
super(initialCapacity, loadFactor, concurrencyLevel);
}
private static class MySoftReference<K,T> extends SoftReference<T> implements ConcurrentRefValueHashMap.MyReference<K,T> {
private final K key;
private MySoftReference(K key, T referent, ReferenceQueue<T> q) {
super(referent, q);
this.key = key;
}
@Override
public K getKey() {
return key;
}
// MUST work with gced references too for the code in processQueue to work
public final boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final ConcurrentRefValueHashMap.MyReference that = (ConcurrentRefValueHashMap.MyReference)o;
return key.equals(that.getKey()) && Comparing.equal(get(), that.get());
}
public final int hashCode() {
return key.hashCode();
}
}
@Override
public V get(Object key) {
MyReference<K,V> ref = myMap.get(key);
if (ref == null) return null;
return ref.get();
}
@Override
public V put(K key, V value) {
processQueue();
MyReference<K,V> oldRef = myMap.put(key, new MyReference<K,V>(key, value, myQueue));
return oldRef != null ? oldRef.get() : null;
}
@Override
public void clear() {
myMap.clear();
}
@Override
public int size() {
return myMap.size(); //?
}
@Override
public boolean isEmpty() {
return myMap.isEmpty(); //?
}
@Override
public boolean containsKey(Object key) {
return get(key) != null;
}
@Override
public boolean containsValue(Object value) {
throw new RuntimeException("method not implemented");
}
@Override
public Set<K> keySet() {
return myMap.keySet();
}
@Override
public Collection<V> values() {
List<V> result = new ArrayList<V>();
final Collection<MyReference<K, V>> refs = myMap.values();
for (MyReference<K, V> ref : refs) {
final V value = ref.get();
if (value != null) {
result.add(value);
}
}
return result;
}
@Override
public Set<Entry<K, V>> entrySet() {
return new AbstractSet<Entry<K, V>>() {
@Override
public Iterator<Entry<K, V>> iterator() {
final Iterator<Entry<K,MyReference<K, V>>> refEntries = myMap.entrySet().iterator();
return new Iterator<Entry<K, V>>() {
Entry<K, V> next;
@Override
public boolean hasNext() {
while (next == null) {
if (!refEntries.hasNext()) return false;
Entry<K, MyReference<K, V>> ref = refEntries.next();
final K k = ref.getKey();
V v = ref.getValue().get();
next = v == null ? null : new AbstractMap.SimpleEntry<K, V>(k,v){
@Override
public V setValue(V value) {
V old = super.setValue(value);
put(k, value);
return old;
}
};
}
return true;
}
@Override
public Entry<K, V> next() {
if (!hasNext()) throw new NoSuchElementException();
Entry<K, V> r = next;
next = null;
return r;
}
@Override
public void remove() {
if (next == null) {
throw new NoSuchElementException();
}
ConcurrentSoftValueHashMap.this.remove(next.getKey());
}
};
}
@Override
public int size() {
return myMap.size();
}
@Override
public boolean remove(Object o) {
return ConcurrentSoftValueHashMap.this.remove(((Entry)o).getKey()) != null;
}
@Override
public void clear() {
ConcurrentSoftValueHashMap.this.clear();
}
};
}
@Override
public V putIfAbsent(@NotNull final K key, final V value) {
while (true) {
processQueue();
MyReference<K, V> newRef = new MyReference<K, V>(key, value, myQueue);
MyReference<K,V> oldRef = myMap.putIfAbsent(key, newRef);
if (oldRef == null) return null;
final V oldVal = oldRef.get();
if (oldVal == null) {
if (myMap.replace(key, oldRef, newRef)) return null;
}
else {
return oldVal;
}
}
}
@Override
public boolean remove(@NotNull final Object key, final Object value) {
processQueue();
return myMap.remove(key, new MyReference<K,V>((K)key, (V)value, myQueue));
}
@Override
public boolean replace(@NotNull final K key, @NotNull final V oldValue, @NotNull final V newValue) {
processQueue();
return myMap.replace(key, new MyReference<K,V>(key, oldValue, myQueue), new MyReference<K,V>(key, newValue, myQueue));
}
@Override
public V replace(@NotNull final K key, @NotNull final V value) {
processQueue();
MyReference<K, V> ref = myMap.replace(key, new MyReference<K, V>(key, value, myQueue));
return ref == null ? null : ref.get();
}
@Override
public V remove(Object key) {
processQueue();
MyReference<K,V> ref = myMap.remove(key);
return ref != null ? ref.get() : null;
}
@Override
public void putAll(Map<? extends K, ? extends V> t) {
for (Entry<? extends K, ? extends V> entry : t.entrySet()) {
V v = entry.getValue();
if (v != null) {
put(entry.getKey(), v);
}
}
protected ConcurrentRefValueHashMap.MyReference<K, V> createRef(K key, V value) {
return new MySoftReference<K,V>(key, value, myQueue);
}
}
@@ -17,6 +17,7 @@
package com.intellij.util.containers;
import com.intellij.openapi.util.Comparing;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.ReferenceQueue;
@@ -41,7 +42,11 @@ public class ConcurrentSoftValueIntObjectHashMap<V> extends ConcurrentRefValueIn
@Override
public boolean equals(Object obj) {
V v = get();
return obj instanceof MyRef && ((MyRef)obj).hash == hash && v != null && v.equals(((MyRef)obj).get());
if (!(obj instanceof MyRef)) {
return false;
}
MyRef other = (MyRef)obj;
return other.hash == hash && key == other.getKey() && Comparing.equal(v, other.get());
}
@Override
@@ -27,385 +27,71 @@ import org.jetbrains.annotations.NotNull;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.Map;
/**
* Fully copied from java.util.WeakHashMap except "get" method optimization.
*/
public final class ConcurrentWeakHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V> {
private interface Key<K>{
K get();
}
private static class WeakKey<K> extends WeakReference<K> implements Key<K> {
public final class ConcurrentWeakHashMap<K,V> extends ConcurrentRefHashMap<K,V> {
private static class WeakKey<K, V> extends WeakReference<K> implements Key<K, V> {
private final int myHash; /* Hashcode of key, stored here since the key may be tossed by the GC */
private final V value;
private WeakKey(K k) {
super(k);
myHash = k.hashCode();
}
public static <K> WeakKey<K> create(K k) {
return k == null ? null : new WeakKey<K>(k);
}
private WeakKey(K k, ReferenceQueue<K> q) {
private WeakKey(@NotNull K k, V v, ReferenceQueue<K> q) {
super(k, q);
value = v;
myHash = k.hashCode();
}
private static <K> WeakKey<K> create(K k, ReferenceQueue<K> q) {
return k == null ? null : new WeakKey<K>(k, q);
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Object t = get();
Object u = ((Key)o).get();
if (t == null || u == null) return false;
if (t == u) return true;
return t.equals(u);
}
public int hashCode() {
return myHash;
}
}
private static class HardKey implements Key {
private Object myObject;
private int myHash;
public HardKey(Object object) {
setObject(object);
}
private void setObject(final Object object) {
myObject = object;
myHash = object != null ? object.hashCode() : 0;
}
@Override
public Object get() {
return myObject;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Object t = get();
Object u = ((Key)o).get();
if (t == null || u == null) return false;
if (t == u) return true;
return t.equals(u);
}
public int hashCode() {
return myHash;
}
}
private final ConcurrentMap<Key<K>, V> myMap;
private static final Key NULL_KEY = new Key() {
@Override
public Object get() {
return null;
}
};
private final ReferenceQueue<K> myReferenceQueue = new ReferenceQueue<K>();
private void processQueue() {
WeakKey wk;
while((wk = (WeakKey)myReferenceQueue.poll()) != null){
myMap.remove(wk);
}
}
public ConcurrentWeakHashMap(int initialCapacity, float loadFactor) {
myMap = new ConcurrentHashMap<Key<K>, V>(initialCapacity, loadFactor, 4);
}
public ConcurrentWeakHashMap(int initialCapacity) {
myMap = new ConcurrentHashMap<Key<K>, V>(initialCapacity);
}
public ConcurrentWeakHashMap() {
myMap = new ConcurrentHashMap<Key<K>, V>();
}
public ConcurrentWeakHashMap(int initialCapacity, float loadFactor, int concurrencyLevel, TObjectHashingStrategy<Key<K>> hashingStrategy) {
myMap = new ConcurrentHashMap<Key<K>, V>(initialCapacity, loadFactor, concurrencyLevel, hashingStrategy);
}
public ConcurrentWeakHashMap(Map<? extends K, ? extends V> t) {
this(Math.max(2 * t.size(), 11), 0.75f);
putAll(t);
}
public ConcurrentWeakHashMap(final TObjectHashingStrategy<K> hashingStrategy) {
myMap = new ConcurrentHashMap<Key<K>, V>(new TObjectHashingStrategy<Key<K>>() {
@Override
public int computeHashCode(final Key<K> object) {
return hashingStrategy.computeHashCode(object.get());
}
@Override
public boolean equals(final Key<K> o1, final Key<K> o2) {
return hashingStrategy.equals(o1.get(), o2.get());
}
} );
}
@Override
public int size() {
return entrySet().size();
}
@Override
public boolean isEmpty() {
return entrySet().isEmpty();
}
@Override
public boolean containsKey(Object key) {
// optimization:
if (key == null){
return myMap.containsKey(NULL_KEY);
}
else{
HardKey hardKey = createHardKey(key);
boolean result = myMap.containsKey(hardKey);
releaseHardKey(hardKey);
return result;
}
//return myMap.containsKey(WeakKey.create(key));
}
private static final ThreadLocal<HardKey> myHardKey = new ThreadLocal<HardKey>(){
@Override
protected HardKey initialValue() {
return new HardKey(null);
}
};
private static HardKey createHardKey(final Object key) {
HardKey hardKey = myHardKey.get();
hardKey.setObject(key);
return hardKey;
}
private static void releaseHardKey(HardKey key) {
key.setObject(null);
}
@Override
public V get(Object key) {
//return myMap.get(WeakKey.create(key));
// optimization:
if (key == null){
return myMap.get(NULL_KEY);
}
else{
HardKey hardKey = createHardKey(key);
V result = myMap.get(hardKey);
releaseHardKey(hardKey);
return result;
}
}
@Override
public V put(K key, V value) {
processQueue();
Key<K> weakKey = WeakKey.create(key, myReferenceQueue);
Key<K> o = weakKey == null ? NULL_KEY : weakKey;
return myMap.put(o, value);
}
@Override
public V remove(Object key) {
processQueue();
// optimization:
if (key == null){
return myMap.remove(NULL_KEY);
}
else{
HardKey hardKey = createHardKey(key);
V result = myMap.remove(hardKey);
releaseHardKey(hardKey);
return result;
}
}
@Override
public void clear() {
processQueue();
myMap.clear();
}
private static class Entry<K,V> implements Map.Entry<K,V> {
private final Map.Entry<?,V> ent;
private final K key; /* Strong reference to key, so that the GC
will leave it alone as long as this Entry
exists */
Entry(Map.Entry<?,V> ent, K key) {
this.ent = ent;
this.key = key;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return ent.getValue();
}
@Override
public V setValue(V value) {
return ent.setValue(value);
}
private static boolean valEquals(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
return value;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry)) return false;
Map.Entry e = (Map.Entry)o;
return valEquals(key, e.getKey()) && valEquals(getValue(), e.getValue());
if (this == o) return true;
if (!(o instanceof Key)) return false;
Object t = get();
Object u = ((Key)o).get();
if (t == null || u == null) return false;
if (t == u) return true;
return t.equals(u);
}
public int hashCode() {
Object v;
return (key == null ? 0 : key.hashCode()) ^ ((v = getValue()) == null ? 0 : v.hashCode());
return myHash;
}
}
/* Internal class for entry sets */
private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
Set<Map.Entry<Key<K>,V>> hashEntrySet = myMap.entrySet();
@Override
public Iterator<Map.Entry<K,V>> iterator() {
return new Iterator<Map.Entry<K,V>>() {
Iterator<Map.Entry<Key<K>,V>> hashIterator = hashEntrySet.iterator();
Entry<K,V> next = null;
@Override
public boolean hasNext() {
while(hashIterator.hasNext()){
Map.Entry<Key<K>, V> ent = hashIterator.next();
WeakKey<K> wk = (WeakKey<K>)ent.getKey();
K k = null;
if (wk != null && (k = wk.get()) == null){
/* Weak key has been cleared by GC */
continue;
}
next = new Entry<K,V>(ent, k);
return true;
}
return false;
}
@Override
public Map.Entry<K, V> next() {
if (next == null && !hasNext()) {
throw new NoSuchElementException();
}
Entry<K,V> e = next;
next = null;
return e;
}
@Override
public void remove() {
hashIterator.remove();
}
};
}
@Override
public boolean isEmpty() {
return !iterator().hasNext();
}
@Override
public int size() {
int j = 0;
for(Iterator i = iterator(); i.hasNext(); i.next()) j++;
return j;
}
@Override
public boolean remove(Object o) {
processQueue();
if (!(o instanceof Map.Entry)) return false;
Map.Entry e = (Map.Entry)o;
Object ev = e.getValue();
HardKey key = createHardKey(o);
V hv = myMap.get(key);
boolean toRemove = hv == null ? ev == null && myMap.containsKey(key) : hv.equals(ev);
if (toRemove){
myMap.remove(key);
}
releaseHardKey(key);
return toRemove;
}
public int hashCode() {
int h = 0;
for (Object aHashEntrySet : hashEntrySet) {
Map.Entry ent = (Map.Entry)aHashEntrySet;
WeakKey wk = (WeakKey)ent.getKey();
if (wk == null) continue;
Object v;
h += wk.hashCode() ^ ((v = ent.getValue()) == null ? 0 : v.hashCode());
}
return h;
}
}
private Set<Map.Entry<K,V>> entrySet = null;
@Override
public Set<Map.Entry<K,V>> entrySet() {
if (entrySet == null) entrySet = new EntrySet();
return entrySet;
}
@Override
public V putIfAbsent(@NotNull final K key, final V value) {
processQueue();
return myMap.putIfAbsent(WeakKey.create(key, myReferenceQueue), value);
protected Key<K, V> createKey(@NotNull K key, V value) {
return new WeakKey<K, V>(key, value, myReferenceQueue);
}
@Override
public boolean remove(@NotNull final Object key, final Object value) {
processQueue();
return myMap.remove(WeakKey.create((K)key, myReferenceQueue), value);
public ConcurrentWeakHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
@Override
public boolean replace(@NotNull final K key, @NotNull final V oldValue, @NotNull final V newValue) {
processQueue();
return myMap.replace(WeakKey.create(key, myReferenceQueue), oldValue,newValue);
public ConcurrentWeakHashMap(int initialCapacity) {
super(initialCapacity);
}
@Override
public V replace(@NotNull final K key, @NotNull final V value) {
processQueue();
return myMap.replace(WeakKey.create(key, myReferenceQueue), value);
public ConcurrentWeakHashMap() {
}
public ConcurrentWeakHashMap(int initialCapacity,
float loadFactor,
int concurrencyLevel,
TObjectHashingStrategy<Key<K, V>> hashingStrategy) {
super(initialCapacity, loadFactor, concurrencyLevel, hashingStrategy);
}
public ConcurrentWeakHashMap(Map<? extends K, ? extends V> t) {
super(t);
}
public ConcurrentWeakHashMap(TObjectHashingStrategy<K> hashingStrategy) {
super(hashingStrategy);
}
}
@@ -17,219 +17,53 @@
package com.intellij.util.containers;
import com.intellij.openapi.util.Comparing;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.Map;
public final class ConcurrentWeakValueHashMap<K,V> implements ConcurrentMap<K,V> {
private final ConcurrentHashMap<K,MyReference<K,V>> myMap;
private final ReferenceQueue<V> myQueue = new ReferenceQueue<V>();
public ConcurrentWeakValueHashMap(final Map<K, V> map) {
this();
putAll(map);
public final class ConcurrentWeakValueHashMap<K,V> extends ConcurrentRefValueHashMap<K,V> {
public ConcurrentWeakValueHashMap(Map<K, V> map) {
super(map);
}
public ConcurrentWeakValueHashMap() {
myMap = new ConcurrentHashMap<K, MyReference<K,V>>();
}
public ConcurrentWeakValueHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
myMap = new ConcurrentHashMap<K, MyReference<K,V>>(initialCapacity, loadFactor, concurrencyLevel);
super();
}
private static class MyReference<K,T> extends WeakReference<T> {
public ConcurrentWeakValueHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
super(initialCapacity, loadFactor, concurrencyLevel);
}
private static class MyWeakReference<K,T> extends WeakReference<T> implements MyReference<K,T> {
private final K key;
public MyReference(K key, T referent, ReferenceQueue<? super T> q) {
private MyWeakReference(K key, T referent, ReferenceQueue<T> q) {
super(referent, q);
this.key = key;
}
public boolean equals(final Object o) {
@Override
public K getKey() {
return key;
}
// MUST work with gced references too for the code in processQueue to work
public final boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyReference that = (MyReference)o;
return key.equals(that.key) && Comparing.equal(get(), that.get());
return key.equals(that.getKey()) && Comparing.equal(get(), that.get());
}
public int hashCode() {
public final int hashCode() {
return key.hashCode();
}
}
private void processQueue() {
while(true){
MyReference<K,V> ref = (MyReference<K,V>)myQueue.poll();
if (ref == null) {
break;
}
if (myMap.get(ref.key) == ref){
myMap.remove(ref.key);
}
}
}
@Override
public V get(@NotNull Object key) {
MyReference<K,V> ref = myMap.get(key);
if (ref == null) return null;
return ref.get();
}
@Override
public V put(@NotNull K key, @NotNull V value) {
processQueue();
MyReference<K,V> oldRef = myMap.put(key, createRef(key, value));
return oldRef != null ? oldRef.get() : null;
}
private MyReference<K, V> createRef(K key, V value) {
return new MyReference<K,V>(key, value, myQueue);
}
@Override
public V putIfAbsent(@NotNull K key, V value) {
while (true) {
processQueue();
MyReference<K, V> newRef = createRef(key, value);
MyReference<K,V> oldRef = myMap.putIfAbsent(key, newRef);
if (oldRef == null) return null;
final V oldVal = oldRef.get();
if (oldVal == null) {
if (myMap.replace(key, oldRef, newRef)) return null;
}
else {
return oldVal;
}
}
}
@Override
public boolean remove(@NotNull final Object key, final Object value) {
processQueue();
return myMap.remove(key, createRef((K)key, (V)value));
}
@Override
public boolean replace(@NotNull final K key, @NotNull final V oldValue, @NotNull final V newValue) {
processQueue();
return myMap.replace(key, createRef(key, oldValue), createRef(key, newValue));
}
@Override
public V replace(@NotNull final K key, @NotNull final V value) {
processQueue();
MyReference<K, V> ref = myMap.replace(key, createRef(key, value));
return ref == null ? null : ref.get();
}
@Override
public V remove(Object key) {
processQueue();
MyReference<K,V> ref = myMap.remove(key);
return ref != null ? ref.get() : null;
}
@Override
public void putAll(Map<? extends K, ? extends V> t) {
processQueue();
for (K k : t.keySet()) {
V v = t.get(k);
if (v != null) {
put(k, v);
}
}
}
@Override
public void clear() {
myMap.clear();
processQueue();
}
@Override
public int size() {
return myMap.size(); //?
}
@Override
public boolean isEmpty() {
return myMap.isEmpty(); //?
}
@Override
public boolean containsKey(Object key) {
return get(key) != null;
}
@Override
public boolean containsValue(Object value) {
throw new RuntimeException("method not implemented");
}
@Override
public Set<K> keySet() {
return myMap.keySet();
}
@Override
public Collection<V> values() {
List<V> result = new ArrayList<V>();
final Collection<MyReference<K, V>> refs = myMap.values();
for (MyReference<K, V> ref : refs) {
final V value = ref.get();
if (value != null) {
result.add(value);
}
}
return result;
}
@Override
public Set<Entry<K, V>> entrySet() {
final Set<K> keys = keySet();
Set<Entry<K, V>> entries = new HashSet<Entry<K, V>>();
for (final K key : keys) {
final V value = get(key);
if (value != null) {
entries.add(new Entry<K, V>() {
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
throw new UnsupportedOperationException("setValue is not implemented");
}
});
}
}
return entries;
}
@Override
public String toString() {
@NonNls String s = "ConcurrentWeakValueHashMap size:" + size() + " [";
for (K k : myMap.keySet()) {
Object v = get(k);
s += "'"+k + "': '" +v+"', ";
}
s += "] ";
return s;
protected MyReference<K, V> createRef(K key, V value) {
return new MyWeakReference<K,V>(key, value, myQueue);
}
}
@@ -17,6 +17,7 @@
package com.intellij.util.containers;
import com.intellij.openapi.util.Comparing;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.ReferenceQueue;
@@ -41,7 +42,11 @@ public class ConcurrentWeakValueIntObjectHashMap<V> extends ConcurrentRefValueIn
@Override
public boolean equals(Object obj) {
V v = get();
return obj instanceof MyRef && ((MyRef)obj).hash == hash && v != null && v.equals(((MyRef)obj).get());
if (!(obj instanceof MyRef)) {
return false;
}
MyRef other = (MyRef)obj;
return other.hash == hash && key == other.getKey() && Comparing.equal(v, other.get());
}
@Override