diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentRefHashMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentRefHashMap.java new file mode 100644 index 000000000000..39d8d96dd3b6 --- /dev/null +++ b/platform/util/src/com/intellij/util/containers/ConcurrentRefHashMap.java @@ -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 extends AbstractMap implements ConcurrentMap { + protected interface Key{ + 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 createKey(@NotNull K key, V value); + + + private static class HardKey implements Key { + 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, 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 myReferenceQueue = new ReferenceQueue(); + + private void processQueue() { + Key wk; + while((wk = (Key)myReferenceQueue.poll()) != null){ + V value = wk.getValue(); + myMap.remove(wk, value); + } + } + + public ConcurrentRefHashMap(int initialCapacity, float loadFactor) { + myMap = new ConcurrentHashMap, V>(initialCapacity, loadFactor, 4); + } + + public ConcurrentRefHashMap(int initialCapacity) { + myMap = new ConcurrentHashMap, V>(initialCapacity); + } + + public ConcurrentRefHashMap() { + myMap = new ConcurrentHashMap, V>(); + } + + public ConcurrentRefHashMap(int initialCapacity, float loadFactor, int concurrencyLevel, TObjectHashingStrategy> hashingStrategy) { + myMap = new ConcurrentHashMap, V>(initialCapacity, loadFactor, concurrencyLevel, hashingStrategy); + } + + public ConcurrentRefHashMap(Map t) { + this(Math.max(2 * t.size(), 11), 0.75f); + putAll(t); + } + + public ConcurrentRefHashMap(final TObjectHashingStrategy hashingStrategy) { + myMap = new ConcurrentHashMap, V>(new TObjectHashingStrategy>() { + @Override + public int computeHashCode(final Key object) { + return hashingStrategy.computeHashCode(object.get()); + } + + @Override + public boolean equals(final Key o1, final Key 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((K)key); + boolean result = myMap.containsKey(hardKey); + releaseHardKey(hardKey); + return result; + } + //return myMap.containsKey(WeakKey.create(key)); + } + + private static final ThreadLocal myHardKey = new ThreadLocal(){ + @Override + protected HardKey initialValue() { + return new HardKey(null, null); + } + }; + + private static HardKey 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 hardKey = createHardKey((K)key); + V result = myMap.get(hardKey); + releaseHardKey(hardKey); + return result; + } + } + + @Override + public V put(K key, V value) { + processQueue(); + Key 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 implements Map.Entry { + private final Map.Entry 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 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> { + Set,V>> hashEntrySet = myMap.entrySet(); + + @Override + public Iterator> iterator() { + return new Iterator>() { + Iterator,V>> hashIterator = hashEntrySet.iterator(); + Entry next = null; + + @Override + public boolean hasNext() { + while(hashIterator.hasNext()){ + Map.Entry, V> ent = hashIterator.next(); + Key wk = ent.getKey(); + K k = null; + if (wk != null && (k = wk.get()) == null){ + /* Weak key has been cleared by GC */ + continue; + } + next = new Entry(ent, k); + return true; + } + return false; + } + + @Override + public Map.Entry 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 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> entrySet = null; + + @Override + public Set> 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); + } +} diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentRefValueHashMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentRefValueHashMap.java new file mode 100644 index 000000000000..a577b82a8f1e --- /dev/null +++ b/platform/util/src/com/intellij/util/containers/ConcurrentRefValueHashMap.java @@ -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 implements ConcurrentMap { + private final ConcurrentHashMap> myMap; + protected final ReferenceQueue myQueue = new ReferenceQueue(); + + public ConcurrentRefValueHashMap(final Map map) { + this(); + putAll(map); + } + + public ConcurrentRefValueHashMap() { + myMap = new ConcurrentHashMap>(); + } + public ConcurrentRefValueHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { + myMap = new ConcurrentHashMap>(initialCapacity, loadFactor, concurrencyLevel); + } + + protected interface MyReference { + K getKey(); + V get(); + } + + private void processQueue() { + while(true){ + MyReference ref = (MyReference)myQueue.poll(); + if (ref == null) break; + myMap.remove(ref.getKey(), ref); + } + } + + @Override + public V get(@NotNull Object key) { + MyReference ref = myMap.get(key); + if (ref == null) return null; + return ref.get(); + } + + @Override + public V put(@NotNull K key, @NotNull V value) { + processQueue(); + MyReference oldRef = myMap.put(key, createRef(key, value)); + return oldRef != null ? oldRef.get() : null; + } + + protected abstract MyReference createRef(K key, V value); + + @Override + public V putIfAbsent(@NotNull K key, V value) { + MyReference newRef = createRef(key, value); + while (true) { + processQueue(); + MyReference 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 ref = myMap.replace(key, createRef(key, value)); + return ref == null ? null : ref.get(); + } + + @Override + public V remove(Object key) { + processQueue(); + MyReference ref = myMap.remove(key); + return ref != null ? ref.get() : null; + } + + @Override + public void putAll(Map 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 keySet() { + return myMap.keySet(); + } + + @Override + public Collection values() { + List result = new ArrayList(); + final Collection> refs = myMap.values(); + for (MyReference ref : refs) { + final V value = ref.get(); + if (value != null) { + result.add(value); + } + } + return result; + } + + @Override + public Set> entrySet() { + final Set keys = keySet(); + Set> entries = new HashSet>(); + + for (final K key : keys) { + final V value = get(key); + if (value != null) { + entries.add(new Entry() { + @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; + } +} diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentRefValueIntObjectHashMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentRefValueIntObjectHashMap.java index 2a10009a31c4..d08dbca2b91b 100644 --- a/platform/util/src/com/intellij/util/containers/ConcurrentRefValueIntObjectHashMap.java +++ b/platform/util/src/com/intellij/util/containers/ConcurrentRefValueIntObjectHashMap.java @@ -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 implements ConcurrentIntObjectMap { private final StripedLockIntObjectConcurrentHashMap> myMap = new StripedLockIntObjectConcurrentHashMap>(); @@ -34,14 +35,15 @@ abstract class ConcurrentRefValueIntObjectHashMap implements ConcurrentIntObj V get(); } - void processQueue() { - while(true){ - IntReference ref = (IntReference)myQueue.poll(); + private void processQueue() { + while (true) { + @SuppressWarnings("unchecked") + IntReference 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 implements ConcurrentIntObj @Override public V cacheOrGet(int key, @NotNull V value) { processQueue(); - IntReference ref = myMap.putIfAbsent(key, createReference(key, value, myQueue)); - V v = ref == null ? null : ref.get(); - return v == null ? value : v; + IntReference newRef = createReference(key, value, myQueue); + while (true) { + IntReference 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 implements ConcurrentIntObj @Override public StripedLockIntObjectConcurrentHashMap.IntEntry next() { + if (!hasNext()) throw new NoSuchElementException(); StripedLockIntObjectConcurrentHashMap.IntEntry result = next; next = nextAliveEntry(); return result; diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentSoftHashMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentSoftHashMap.java index c36997f7e0c7..d13f0e3c4750 100644 --- a/platform/util/src/com/intellij/util/containers/ConcurrentSoftHashMap.java +++ b/platform/util/src/com/intellij/util/containers/ConcurrentSoftHashMap.java @@ -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 extends AbstractMap implements ConcurrentMap { - private interface Key{ - K get(); - } - - private static class SoftKey extends SoftReference implements Key { +public final class ConcurrentSoftHashMap extends ConcurrentRefHashMap { + private static class SoftKey extends SoftReference implements ConcurrentRefHashMap.Key { 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 SoftKey create(K k) { - return k == null ? null : new SoftKey(k); - } - - private SoftKey(K k, ReferenceQueue q) { + private SoftKey(@NotNull K k, V v, ReferenceQueue q) { super(k, q); + value = v; myHash = k.hashCode(); } - private static SoftKey create(K k, ReferenceQueue q) { - return k == null ? null : new SoftKey(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 implements Key { - 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, V> myMap; - private static final Key NULL_KEY = new HardKey(""); - - private final ReferenceQueue myReferenceQueue = new ReferenceQueue(); - - private void processQueue() { - SoftKey wk; - while((wk = (SoftKey)myReferenceQueue.poll()) != null){ - myMap.remove(wk); - } - } - - public ConcurrentSoftHashMap(int initialCapacity, float loadFactor) { - myMap = new ConcurrentHashMap, V>(initialCapacity, loadFactor, 4); - } - - public ConcurrentSoftHashMap(int initialCapacity) { - myMap = new ConcurrentHashMap, V>(initialCapacity); - } - - public ConcurrentSoftHashMap() { - myMap = new ConcurrentHashMap, V>(); - } - - public ConcurrentSoftHashMap(@NotNull Map t) { - this(Math.max(2 * t.size(), 11), 0.75f); - putAll(t); - } - - public ConcurrentSoftHashMap(final TObjectHashingStrategy hashingStrategy) { - myMap = new ConcurrentHashMap, V>(new TObjectHashingStrategy>() { - @Override - public int computeHashCode(final Key object) { - return hashingStrategy.computeHashCode(object.get()); - } - - @Override - public boolean equals(final Key o1, final Key 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 = new HardKey(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 hardKey = new HardKey(key); - V result = myMap.get(hardKey); - return (V)result; - } - } - - @Override - public V put(K key, V value) { - processQueue(); - SoftKey softKey = SoftKey.create(key, myReferenceQueue); - Key sk = softKey == null ? (Key)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 hardKey = new HardKey(key); - return myMap.remove(hardKey); - } - //return myMap.remove(SoftKey.create(key)); - } - - @Override - public void clear() { - processQueue(); - myMap.clear(); - } - - private static class Entry implements Map.Entry { - private final Map.Entry 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 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 e = (Map.Entry)o; - V ev = e.getValue(); - - // optimization: - HardKey key = new HardKey(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> entrySet = null; - - @Override - public Set> 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 createKey(@NotNull K key, V value) { + return new SoftKey(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> hashingStrategy) { + super(initialCapacity, loadFactor, concurrencyLevel, hashingStrategy); + } + + public ConcurrentSoftHashMap(Map t) { + super(t); + } + + public ConcurrentSoftHashMap(TObjectHashingStrategy hashingStrategy) { + super(hashingStrategy); } } diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentSoftValueHashMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentSoftValueHashMap.java index 2642311b0df6..511bcf4fd605 100644 --- a/platform/util/src/com/intellij/util/containers/ConcurrentSoftValueHashMap.java +++ b/platform/util/src/com/intellij/util/containers/ConcurrentSoftValueHashMap.java @@ -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 implements ConcurrentMap { - private final ConcurrentMap> myMap = new ConcurrentHashMap>(); - private final ReferenceQueue> myQueue = new ReferenceQueue>(); - - private static class MyReference extends SoftReference { - private final K key; - private MyReference(K key, V referent, ReferenceQueue q) { - super(referent, (ReferenceQueue)q); - this.key = key; - } +public final class ConcurrentSoftValueHashMap extends ConcurrentRefValueHashMap { + public ConcurrentSoftValueHashMap(Map map) { + super(map); } public ConcurrentSoftValueHashMap() { + super(); } - private void processQueue() { - while(true){ - MyReference ref = (MyReference)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 extends SoftReference implements ConcurrentRefValueHashMap.MyReference { + private final K key; + private MySoftReference(K key, T referent, ReferenceQueue 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 ref = myMap.get(key); - if (ref == null) return null; - return ref.get(); - } - - @Override - public V put(K key, V value) { - processQueue(); - MyReference oldRef = myMap.put(key, new MyReference(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 keySet() { - return myMap.keySet(); - } - - @Override - public Collection values() { - List result = new ArrayList(); - final Collection> refs = myMap.values(); - for (MyReference ref : refs) { - final V value = ref.get(); - if (value != null) { - result.add(value); - } - } - return result; - } - - @Override - public Set> entrySet() { - return new AbstractSet>() { - @Override - public Iterator> iterator() { - final Iterator>> refEntries = myMap.entrySet().iterator(); - return new Iterator>() { - Entry next; - @Override - public boolean hasNext() { - while (next == null) { - if (!refEntries.hasNext()) return false; - Entry> ref = refEntries.next(); - final K k = ref.getKey(); - V v = ref.getValue().get(); - next = v == null ? null : new AbstractMap.SimpleEntry(k,v){ - @Override - public V setValue(V value) { - V old = super.setValue(value); - put(k, value); - return old; - } - }; - } - return true; - } - - @Override - public Entry next() { - if (!hasNext()) throw new NoSuchElementException(); - Entry 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 newRef = new MyReference(key, value, myQueue); - MyReference 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)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(key, oldValue, myQueue), new MyReference(key, newValue, myQueue)); - } - - @Override - public V replace(@NotNull final K key, @NotNull final V value) { - processQueue(); - MyReference ref = myMap.replace(key, new MyReference(key, value, myQueue)); - return ref == null ? null : ref.get(); - } - - @Override - public V remove(Object key) { - processQueue(); - MyReference ref = myMap.remove(key); - return ref != null ? ref.get() : null; - } - - @Override - public void putAll(Map t) { - for (Entry entry : t.entrySet()) { - V v = entry.getValue(); - if (v != null) { - put(entry.getKey(), v); - } - } + protected ConcurrentRefValueHashMap.MyReference createRef(K key, V value) { + return new MySoftReference(key, value, myQueue); } } diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentSoftValueIntObjectHashMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentSoftValueIntObjectHashMap.java index 1d2643cec415..dad430d35aed 100644 --- a/platform/util/src/com/intellij/util/containers/ConcurrentSoftValueIntObjectHashMap.java +++ b/platform/util/src/com/intellij/util/containers/ConcurrentSoftValueIntObjectHashMap.java @@ -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 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 diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentWeakHashMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentWeakHashMap.java index 3319ef1ccccd..16c276a127e7 100644 --- a/platform/util/src/com/intellij/util/containers/ConcurrentWeakHashMap.java +++ b/platform/util/src/com/intellij/util/containers/ConcurrentWeakHashMap.java @@ -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 extends AbstractMap implements ConcurrentMap { - private interface Key{ - K get(); - } - - private static class WeakKey extends WeakReference implements Key { +public final class ConcurrentWeakHashMap extends ConcurrentRefHashMap { + private static class WeakKey extends WeakReference implements Key { 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 WeakKey create(K k) { - return k == null ? null : new WeakKey(k); - } - - private WeakKey(K k, ReferenceQueue q) { + private WeakKey(@NotNull K k, V v, ReferenceQueue q) { super(k, q); + value = v; myHash = k.hashCode(); } - private static WeakKey create(K k, ReferenceQueue q) { - return k == null ? null : new WeakKey(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, V> myMap; - private static final Key NULL_KEY = new Key() { - @Override - public Object get() { - return null; - } - }; - - private final ReferenceQueue myReferenceQueue = new ReferenceQueue(); - - private void processQueue() { - WeakKey wk; - while((wk = (WeakKey)myReferenceQueue.poll()) != null){ - myMap.remove(wk); - } - } - - public ConcurrentWeakHashMap(int initialCapacity, float loadFactor) { - myMap = new ConcurrentHashMap, V>(initialCapacity, loadFactor, 4); - } - - public ConcurrentWeakHashMap(int initialCapacity) { - myMap = new ConcurrentHashMap, V>(initialCapacity); - } - - public ConcurrentWeakHashMap() { - myMap = new ConcurrentHashMap, V>(); - } - - public ConcurrentWeakHashMap(int initialCapacity, float loadFactor, int concurrencyLevel, TObjectHashingStrategy> hashingStrategy) { - myMap = new ConcurrentHashMap, V>(initialCapacity, loadFactor, concurrencyLevel, hashingStrategy); - } - - public ConcurrentWeakHashMap(Map t) { - this(Math.max(2 * t.size(), 11), 0.75f); - putAll(t); - } - - public ConcurrentWeakHashMap(final TObjectHashingStrategy hashingStrategy) { - myMap = new ConcurrentHashMap, V>(new TObjectHashingStrategy>() { - @Override - public int computeHashCode(final Key object) { - return hashingStrategy.computeHashCode(object.get()); - } - - @Override - public boolean equals(final Key o1, final Key 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 myHardKey = new ThreadLocal(){ - @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 weakKey = WeakKey.create(key, myReferenceQueue); - Key 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 implements Map.Entry { - private final Map.Entry 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 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> { - Set,V>> hashEntrySet = myMap.entrySet(); - - @Override - public Iterator> iterator() { - return new Iterator>() { - Iterator,V>> hashIterator = hashEntrySet.iterator(); - Entry next = null; - - @Override - public boolean hasNext() { - while(hashIterator.hasNext()){ - Map.Entry, V> ent = hashIterator.next(); - WeakKey wk = (WeakKey)ent.getKey(); - K k = null; - if (wk != null && (k = wk.get()) == null){ - /* Weak key has been cleared by GC */ - continue; - } - next = new Entry(ent, k); - return true; - } - return false; - } - - @Override - public Map.Entry 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 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> entrySet = null; - - @Override - public Set> 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 createKey(@NotNull K key, V value) { + return new WeakKey(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> hashingStrategy) { + super(initialCapacity, loadFactor, concurrencyLevel, hashingStrategy); + } + + public ConcurrentWeakHashMap(Map t) { + super(t); + } + + public ConcurrentWeakHashMap(TObjectHashingStrategy hashingStrategy) { + super(hashingStrategy); + } + } diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentWeakValueHashMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentWeakValueHashMap.java index f6640b9f3bce..c64f58f3f8c4 100644 --- a/platform/util/src/com/intellij/util/containers/ConcurrentWeakValueHashMap.java +++ b/platform/util/src/com/intellij/util/containers/ConcurrentWeakValueHashMap.java @@ -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 implements ConcurrentMap { - private final ConcurrentHashMap> myMap; - private final ReferenceQueue myQueue = new ReferenceQueue(); - - public ConcurrentWeakValueHashMap(final Map map) { - this(); - putAll(map); +public final class ConcurrentWeakValueHashMap extends ConcurrentRefValueHashMap { + public ConcurrentWeakValueHashMap(Map map) { + super(map); } public ConcurrentWeakValueHashMap() { - myMap = new ConcurrentHashMap>(); - } - public ConcurrentWeakValueHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { - myMap = new ConcurrentHashMap>(initialCapacity, loadFactor, concurrencyLevel); + super(); } - private static class MyReference extends WeakReference { + public ConcurrentWeakValueHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { + super(initialCapacity, loadFactor, concurrencyLevel); + } + + private static class MyWeakReference extends WeakReference implements MyReference { private final K key; - public MyReference(K key, T referent, ReferenceQueue q) { + private MyWeakReference(K key, T referent, ReferenceQueue 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 ref = (MyReference)myQueue.poll(); - if (ref == null) { - break; - } - if (myMap.get(ref.key) == ref){ - myMap.remove(ref.key); - } - } - } - @Override - public V get(@NotNull Object key) { - MyReference ref = myMap.get(key); - if (ref == null) return null; - return ref.get(); - } - - @Override - public V put(@NotNull K key, @NotNull V value) { - processQueue(); - MyReference oldRef = myMap.put(key, createRef(key, value)); - return oldRef != null ? oldRef.get() : null; - } - - private MyReference createRef(K key, V value) { - return new MyReference(key, value, myQueue); - } - - @Override - public V putIfAbsent(@NotNull K key, V value) { - while (true) { - processQueue(); - MyReference newRef = createRef(key, value); - MyReference 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 ref = myMap.replace(key, createRef(key, value)); - return ref == null ? null : ref.get(); - } - - @Override - public V remove(Object key) { - processQueue(); - MyReference ref = myMap.remove(key); - return ref != null ? ref.get() : null; - } - - @Override - public void putAll(Map 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 keySet() { - return myMap.keySet(); - } - - @Override - public Collection values() { - List result = new ArrayList(); - final Collection> refs = myMap.values(); - for (MyReference ref : refs) { - final V value = ref.get(); - if (value != null) { - result.add(value); - } - } - return result; - } - - @Override - public Set> entrySet() { - final Set keys = keySet(); - Set> entries = new HashSet>(); - - for (final K key : keys) { - final V value = get(key); - if (value != null) { - entries.add(new Entry() { - @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 createRef(K key, V value) { + return new MyWeakReference(key, value, myQueue); } } diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentWeakValueIntObjectHashMap.java b/platform/util/src/com/intellij/util/containers/ConcurrentWeakValueIntObjectHashMap.java index 861e47ac781d..325943cb3ac1 100644 --- a/platform/util/src/com/intellij/util/containers/ConcurrentWeakValueIntObjectHashMap.java +++ b/platform/util/src/com/intellij/util/containers/ConcurrentWeakValueIntObjectHashMap.java @@ -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 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