diff --git a/platform/platform-tests/testSrc/com/intellij/util/containers/ConcurrentMapsTest.java b/platform/platform-tests/testSrc/com/intellij/util/containers/ConcurrentMapsTest.java index 15afb08fe205..918d2a607a00 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/containers/ConcurrentMapsTest.java +++ b/platform/platform-tests/testSrc/com/intellij/util/containers/ConcurrentMapsTest.java @@ -15,7 +15,9 @@ */ package com.intellij.util.containers; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.testFramework.UsefulTestCase; import com.intellij.util.GCUtil; import gnu.trove.TObjectHashingStrategy; import org.junit.Test; @@ -25,6 +27,7 @@ import java.util.Set; import static org.junit.Assert.*; +@SuppressWarnings("deprecation") public class ConcurrentMapsTest { private static final long TIMEOUT = 5 * 60 * 1000; // 5 minutes @@ -41,8 +44,8 @@ public class ConcurrentMapsTest { }; @Test(timeout = TIMEOUT) - public void testKeysRemovedWhenIdentityStrategyIsUsed() { - @SuppressWarnings("unchecked") ConcurrentWeakHashMap map = new ConcurrentWeakHashMap(TObjectHashingStrategy.IDENTITY); + public void testConcurrentWeakKeysRemovedWhenIdentityStrategyIsUsed() { + ConcurrentWeakHashMap map = new ConcurrentWeakHashMap<>(ContainerUtil.identityStrategy()); map.put(new Object(), new Object()); do { @@ -50,13 +53,34 @@ public class ConcurrentMapsTest { System.gc(); } while (!map.processQueue()); + assertEquals(0, map.underlyingMapSize()); map.put(this, this); assertEquals(1, map.underlyingMapSize()); } + @Test(timeout = TIMEOUT) + public void testWeakHashMapWithIdentityStrategy() { + WeakHashMap map = new WeakHashMap<>(10,0.5f,ContainerUtil.identityStrategy()); + Ref key = Ref.create(new Object()); + Ref value = Ref.create(new Object()); + map.put(key.get(), value.get()); + + assertSame(value.get(), map.get(key.get())); + value.set(null); + key.set(null); + do { + tryGcSoftlyReachableObjects(); // sometimes weak references are not collected under linux, try to stress gc to force them + System.gc(); + } + while (!map.processQueue()); + assertEquals(0, map.underlyingMapSize()); + UsefulTestCase.assertEmpty(map.keySet()); + assertTrue(map.isEmpty()); + } + @Test(timeout = TIMEOUT) public void testRemoveFromSoftEntrySet() { - ConcurrentSoftHashMap map = new ConcurrentSoftHashMap(); + ConcurrentSoftHashMap map = new ConcurrentSoftHashMap<>(); map.put(this, this); Set> entries = map.entrySet(); assertEquals(1, entries.size()); @@ -68,7 +92,7 @@ public class ConcurrentMapsTest { @Test(timeout = TIMEOUT) public void testRemoveFromWeakEntrySet() { - ConcurrentWeakHashMap map = new ConcurrentWeakHashMap(); + ConcurrentWeakHashMap map = new ConcurrentWeakHashMap<>(); map.put(this, this); Set> entries = map.entrySet(); assertEquals(1, entries.size()); @@ -79,8 +103,8 @@ public class ConcurrentMapsTest { } @Test(timeout = TIMEOUT) - public void testTossedWeakKeysAreRemoved() { - ConcurrentWeakHashMap map = new ConcurrentWeakHashMap(); + public void testConcurrentWeakTossedWeakKeysAreRemoved() { + ConcurrentWeakHashMap map = new ConcurrentWeakHashMap<>(); map.put(new Object(), new Object()); do { @@ -98,8 +122,8 @@ public class ConcurrentMapsTest { } @Test(timeout = TIMEOUT) - public void testTossedSoftKeysAreRemoved() { - ConcurrentSoftHashMap map = new ConcurrentSoftHashMap(); + public void testConcurrentSoftTossedSoftKeysAreRemoved() { + ConcurrentSoftHashMap map = new ConcurrentSoftHashMap<>(); map.put(new Object(), new Object()); do { @@ -113,7 +137,7 @@ public class ConcurrentMapsTest { } @Test(timeout = TIMEOUT) - public void testTossedWeakValueIsRemoved() { + public void testConcurrentWeakTossedWeakValueIsRemoved() { ConcurrentWeakValueHashMap map = (ConcurrentWeakValueHashMap)ContainerUtil.createConcurrentWeakValueMap(); map.put(new Object(), new Object()); @@ -129,8 +153,8 @@ public class ConcurrentMapsTest { } @Test(timeout = TIMEOUT) - public void testTossedSoftValueIsRemoved() { - ConcurrentSoftValueHashMap map = new ConcurrentSoftValueHashMap(); + public void testConcurrentSoftTossedSoftValueIsRemoved() { + ConcurrentSoftValueHashMap map = new ConcurrentSoftValueHashMap<>(); map.put(new Object(), new Object()); do { @@ -144,8 +168,8 @@ public class ConcurrentMapsTest { } @Test(timeout = TIMEOUT) - public void testCustomStrategy() { - SoftHashMap map = new SoftHashMap(CUSTOM_STRATEGY); + public void testSoftMapCustomStrategy() { + SoftHashMap map = new SoftHashMap<>(CUSTOM_STRATEGY); map.put("ab", "ab"); assertTrue(map.containsKey("AB")); @@ -155,8 +179,52 @@ public class ConcurrentMapsTest { } @Test(timeout = TIMEOUT) - public void testCustomStrategyForConcurrentSoft() { - ConcurrentSoftHashMap map = new ConcurrentSoftHashMap(CUSTOM_STRATEGY); + public void testWeakMapCustomStrategy() { + WeakHashMap map = new WeakHashMap<>(10,0.5f,CUSTOM_STRATEGY); + + String keyL = "ab"; + String keyU = "AB"; + String value = "asdfab"; + map.put(keyL, value); + assertTrue(map.containsKey(keyU)); + assertTrue(map.containsKey(keyL)); + String removed = map.remove("aB"); + assertSame(value, removed); + assertTrue(map.isEmpty()); + } + + @Test(timeout = TIMEOUT) + public void testWeakNativeHashCodeDoesNotGetCalledWhenCustomStrategyIsSpecified() { + Map map = new WeakHashMap<>(ContainerUtil.identityStrategy()); + + checkHashCodeDoesntCalledFor(map); + } + + @Test(timeout = TIMEOUT) + public void testSoftNativeHashCodeDoesNotGetCalledWhenCustomStrategyIsSpecified() { + Map map = new SoftHashMap<>(ContainerUtil.identityStrategy()); + + checkHashCodeDoesntCalledFor(map); + } + + private void checkHashCodeDoesntCalledFor(Map map) { + Object key = new Object(){ + @Override + public int hashCode() { + fail("must not be called"); + return super.hashCode(); + } + }; + map.put(key, "ab"); + assertTrue(map.containsKey(key)); + map.remove(key); + assertTrue(map.isEmpty()); + } + + + @Test(timeout = TIMEOUT) + public void testConcurrentSoftCustomStrategy() { + ConcurrentSoftHashMap map = new ConcurrentSoftHashMap<>(CUSTOM_STRATEGY); map.put("ab", "ab"); assertEquals(1, map.size()); @@ -167,28 +235,28 @@ public class ConcurrentMapsTest { } @Test - public void testNullKeyForConcurrentSoft() { - Map map = new ConcurrentSoftHashMap(); + public void testConcurrentSoftNullKey() { + Map map = new ConcurrentSoftHashMap<>(); checkNullKeys(map); } @Test - public void testNullKeyForConcurrentWeak() { - Map map = new ConcurrentWeakHashMap(); + public void testConcurrentWeakNullKey() { + Map map = new ConcurrentWeakHashMap<>(); checkNullKeys(map); } @Test(expected = IllegalArgumentException.class) - public void testNullKeyForConcurrentWeakSoft() { - Map map = new ConcurrentWeakKeySoftValueHashMap(1, 1, 1, CUSTOM_STRATEGY); + public void testConcurrentWeakSoftNullKey() { + Map map = new ConcurrentWeakKeySoftValueHashMap<>(1, 1, 1, CUSTOM_STRATEGY); checkNullKeys(map); } @Test(expected = IllegalArgumentException.class) - public void testNullKeyForConcurrentWeakWeak() { - Map map = new ConcurrentWeakKeyWeakValueHashMap(1, 1, 1, CUSTOM_STRATEGY); + public void testConcurrentWeakWeakNullKey() { + Map map = new ConcurrentWeakKeyWeakValueHashMap<>(1, 1, 1, CUSTOM_STRATEGY); checkNullKeys(map); } @@ -203,8 +271,8 @@ public class ConcurrentMapsTest { } @Test(timeout = TIMEOUT) - public void testCustomStrategyForConcurrentWeakSoft() { - ConcurrentWeakKeySoftValueHashMap map = new ConcurrentWeakKeySoftValueHashMap(1, 1, 1, CUSTOM_STRATEGY); + public void testConcurrentWeakSoftCustomStrategy() { + ConcurrentWeakKeySoftValueHashMap map = new ConcurrentWeakKeySoftValueHashMap<>(1, 1, 1, CUSTOM_STRATEGY); map.put("ab", "ab"); assertEquals(1, map.size()); @@ -215,8 +283,8 @@ public class ConcurrentMapsTest { } @Test(timeout = TIMEOUT) - public void testTossedSoftKeyAndValue() { - SoftKeySoftValueHashMap map = new SoftKeySoftValueHashMap(); + public void testSoftKeySoftValueTossedSoftKeyAndValue() { + SoftKeySoftValueHashMap map = new SoftKeySoftValueHashMap<>(); map.put(new Object(), new Object()); do { @@ -228,8 +296,8 @@ public class ConcurrentMapsTest { } @Test(timeout = TIMEOUT) - public void testTossedWeakKeyAndValue() { - WeakKeyWeakValueHashMap map = new WeakKeyWeakValueHashMap(); + public void testWeakKeyWeakValueTossedWeakKeyAndValue() { + WeakKeyWeakValueHashMap map = new WeakKeyWeakValueHashMap<>(); map.put(new Object(), new Object()); do { @@ -242,7 +310,7 @@ public class ConcurrentMapsTest { @Test(timeout = TIMEOUT) public void testConcurrentLongObjectHashMap() { - ConcurrentLongObjectMap map = new ConcurrentLongObjectHashMap(); + ConcurrentLongObjectMap map = new ConcurrentLongObjectHashMap<>(); for (int i = 0; i < 1000; i++) { Object prev = map.put(i, i); assertNull(prev); @@ -264,7 +332,7 @@ public class ConcurrentMapsTest { @Test(timeout = TIMEOUT) public void testStripedLockIntObjectConcurrentHashMap() { - ConcurrentIntObjectMap map = new StripedLockIntObjectConcurrentHashMap(); + ConcurrentIntObjectMap map = new StripedLockIntObjectConcurrentHashMap<>(); for (int i = 0; i < 1000; i++) { Object prev = map.put(i, i); assertNull(prev); @@ -285,7 +353,7 @@ public class ConcurrentMapsTest { @Test(timeout = TIMEOUT) public void testConcurrentIntObjectHashMap() { - ConcurrentIntObjectMap map = new ConcurrentIntObjectHashMap(); + ConcurrentIntObjectMap map = new ConcurrentIntObjectHashMap<>(); for (int i = 0; i < 1000; i++) { Object prev = map.put(i, i); assertNull(prev); @@ -305,7 +373,7 @@ public class ConcurrentMapsTest { } @Test(timeout = TIMEOUT) - public void testTossedConcurrentWeakKeyAndValue() { + public void testConcurrentWeakKeyAndValueTossed() { ConcurrentWeakKeyWeakValueHashMap map = (ConcurrentWeakKeyWeakValueHashMap)ContainerUtil.createConcurrentWeakKeyWeakValueMap(); map.put(new Object(), new Object()); diff --git a/platform/util/src/com/intellij/util/containers/RefHashMap.java b/platform/util/src/com/intellij/util/containers/RefHashMap.java index 7c2581f25154..c718205f3841 100644 --- a/platform/util/src/com/intellij/util/containers/RefHashMap.java +++ b/platform/util/src/com/intellij/util/containers/RefHashMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -18,6 +18,7 @@ package com.intellij.util.containers; import gnu.trove.THashMap; import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.TestOnly; import java.lang.ref.ReferenceQueue; import java.util.*; @@ -30,48 +31,55 @@ import java.util.*; abstract class RefHashMap extends AbstractMap implements Map { private final MyMap myMap; private final ReferenceQueue myReferenceQueue = new ReferenceQueue(); - private final HardKey myHardKeyInstance = new HardKey(); // "singleton" - private Set> entrySet = null; + private final HardKey myHardKeyInstance = new HardKey(); // "singleton" + @NotNull + private final TObjectHashingStrategy myStrategy; + private Set> entrySet; private boolean processingQueue; - public RefHashMap(int initialCapacity, float loadFactor, @NotNull TObjectHashingStrategy> strategy) { - myMap = new MyMap(initialCapacity, loadFactor, strategy); + RefHashMap(int initialCapacity, float loadFactor, @NotNull final TObjectHashingStrategy strategy) { + myStrategy = strategy; + myMap = new MyMap(initialCapacity, loadFactor); } - public RefHashMap(int initialCapacity, float loadFactor) { - this(initialCapacity, loadFactor, ContainerUtil.>canonicalStrategy()); + RefHashMap(int initialCapacity, float loadFactor) { + this(initialCapacity, loadFactor, ContainerUtil.canonicalStrategy()); } - public RefHashMap(int initialCapacity) { + RefHashMap(int initialCapacity) { this(initialCapacity, 0.8f); } - public RefHashMap() { + RefHashMap() { this(4); } - public RefHashMap(@NotNull Map t) { + RefHashMap(@NotNull Map t) { this(Math.max(2 * t.size(), 11), 0.75f); putAll(t); } - public RefHashMap(@NotNull final TObjectHashingStrategy hashingStrategy) { - this(4, 0.8f, new TObjectHashingStrategy>() { - @Override - public int computeHashCode(final Key object) { - return hashingStrategy.computeHashCode(object.get()); - } + RefHashMap(@NotNull final TObjectHashingStrategy hashingStrategy) { + this(4, 0.8f, hashingStrategy); + } - @Override - public boolean equals(final Key o1, final Key o2) { - return hashingStrategy.equals(o1.get(), o2.get()); - } - }); + static boolean keyEqual(K k1, K k2, TObjectHashingStrategy strategy) { + return k1 == k2 || strategy.equals(k1, k2); } private class MyMap extends THashMap, V> { - private MyMap(int initialCapacity, float loadFactor, @NotNull TObjectHashingStrategy> strategy) { - super(initialCapacity, loadFactor, strategy); + private MyMap(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor, new TObjectHashingStrategy>() { + @Override + public int computeHashCode(final Key key) { + return key.hashCode(); // use stored hashCode + } + + @Override + public boolean equals(final Key o1, final Key o2) { + return o1 == o2 || keyEqual(o1.get(), o2.get(), myStrategy); + } + }); } @Override @@ -123,33 +131,32 @@ abstract class RefHashMap extends AbstractMap implements Map { } @NotNull - protected abstract Key createKey(@NotNull T k, @NotNull ReferenceQueue q); + protected abstract Key createKey(@NotNull T k, @NotNull TObjectHashingStrategy strategy, @NotNull ReferenceQueue q); - private static class HardKey implements Key { - private T myObject; + private class HardKey implements Key { + private K myObject; private int myHash; @Override - public T get() { + public K get() { return myObject; } - private void set(@NotNull T object) { + private void set(@NotNull K object) { myObject = object; - myHash = object.hashCode(); + myHash = myStrategy.computeHashCode(object); } private void clear() { myObject = null; - myHash = 0; } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Key)) return false; - T t = myObject; - T u = ((Key)o).get(); - return t == u || t.equals(u); + K t = myObject; + K u = ((Key)o).get(); + return keyEqual(t, u, myStrategy); } public int hashCode() { @@ -164,7 +171,7 @@ abstract class RefHashMap extends AbstractMap implements Map { processingQueue = true; Key wk; while ((wk = (Key)myReferenceQueue.poll()) != null) { - myMap.remove(wk); + removeKey(wk); processed = true; } } @@ -181,7 +188,7 @@ abstract class RefHashMap extends AbstractMap implements Map { @NotNull Key createKey(@NotNull K key) { - return createKey(key, myReferenceQueue); + return createKey(key, myStrategy, myReferenceQueue); } V putKey(@NotNull Key weakKey, V value) { @@ -242,13 +249,15 @@ abstract class RefHashMap extends AbstractMap implements Map { private static class MyEntry implements Entry { private final Entry ent; - private final K key; /* Strong reference to key, so that the GC - will leave it alone as long as this Entry - exists */ + private final K key; // Strong reference to key, so that the GC will leave it alone as long as this Entry exists + private final int myKeyHashCode; + @NotNull private final TObjectHashingStrategy myStrategy; - private MyEntry(@NotNull Entry ent, K key) { + private MyEntry(@NotNull Entry ent, @NotNull K key, int keyHashCode, @NotNull TObjectHashingStrategy strategy) { this.ent = ent; this.key = key; + myKeyHashCode = keyHashCode; + myStrategy = strategy; } @Override @@ -266,20 +275,19 @@ abstract class RefHashMap extends AbstractMap implements Map { return ent.setValue(value); } - private static boolean valEquals(Object o1, Object o2) { + private static boolean valEqual(Object o1, Object o2) { return o1 == null ? o2 == null : o1.equals(o2); } public boolean equals(Object o) { if (!(o instanceof Entry)) return false; - Entry e = (Entry)o; - return valEquals(key, e.getKey()) && valEquals(getValue(), e.getValue()); + Entry e = (Entry)o; + return keyEqual(key, e.getKey(), myStrategy) && valEqual(getValue(), e.getValue()); } public int hashCode() { V v; - return (key == null ? 0 : key.hashCode()) - ^ ((v = getValue()) == null ? 0 : v.hashCode()); + return myKeyHashCode ^ ((v = getValue()) == null ? 0 : v.hashCode()); } } @@ -292,19 +300,19 @@ abstract class RefHashMap extends AbstractMap implements Map { public Iterator> iterator() { return new Iterator>() { private final Iterator, V>> hashIterator = hashEntrySet.iterator(); - private MyEntry next = null; + private MyEntry next; @Override public boolean hasNext() { while (hashIterator.hasNext()) { 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 */ + K k; + if ((k = wk.get()) == null) { + // weak key has been cleared by GC, ignore continue; } - next = new MyEntry(ent, k); + next = new MyEntry(ent, k, wk.hashCode(), myStrategy); return true; } return false; @@ -361,8 +369,8 @@ abstract class RefHashMap extends AbstractMap implements Map { public int hashCode() { int h = 0; - for (Entry entry : hashEntrySet) { - Key wk = (Key)entry.getKey(); + for (Entry,V> entry : hashEntrySet) { + Key wk = entry.getKey(); if (wk == null) continue; Object v; h += wk.hashCode() ^ ((v = entry.getValue()) == null ? 0 : v.hashCode()); @@ -378,4 +386,9 @@ abstract class RefHashMap extends AbstractMap implements Map { if (entrySet == null) entrySet = new EntrySet(); return entrySet; } + + @TestOnly + int underlyingMapSize() { + return myMap.size(); + } } diff --git a/platform/util/src/com/intellij/util/containers/SoftHashMap.java b/platform/util/src/com/intellij/util/containers/SoftHashMap.java index e886f771ff12..a949eace009a 100644 --- a/platform/util/src/com/intellij/util/containers/SoftHashMap.java +++ b/platform/util/src/com/intellij/util/containers/SoftHashMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -25,7 +25,8 @@ import java.util.Map; /** * Soft keys hash map. - * Null keys are not supported. + * Null keys are NOT allowed + * Null values are allowed */ public final class SoftHashMap extends RefHashMap { public SoftHashMap(int initialCapacity, float loadFactor) { @@ -37,7 +38,6 @@ public final class SoftHashMap extends RefHashMap { } public SoftHashMap() { - super(); } public SoftHashMap(@NotNull Map t) { @@ -48,29 +48,34 @@ public final class SoftHashMap extends RefHashMap { super(hashingStrategy); } + public SoftHashMap(int initialCapacity, float loadFactor, @NotNull TObjectHashingStrategy strategy) { + super(initialCapacity, loadFactor, strategy); + } + @NotNull @Override - protected Key createKey(@NotNull T k, @NotNull ReferenceQueue q) { - return new SoftKey(k, q); + protected Key createKey(@NotNull T k, @NotNull TObjectHashingStrategy strategy, @NotNull ReferenceQueue q) { + return new SoftKey(k, strategy, q); } private static class SoftKey extends SoftReference implements Key { private final int myHash; /* Hash code of key, stored here since the key may be tossed by the GC */ + @NotNull private final TObjectHashingStrategy myStrategy; - private SoftKey(@NotNull T k, @NotNull ReferenceQueue q) { + private SoftKey(@NotNull T k, @NotNull TObjectHashingStrategy strategy, @NotNull ReferenceQueue q) { super(k, q); - myHash = k.hashCode(); + myStrategy = strategy; + myHash = strategy.computeHashCode(k); } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Key)) return false; if (myHash != o.hashCode()) return false; - Object t = get(); - Object u = ((Key)o).get(); + T t = get(); + T u = ((Key)o).get(); if (t == null || u == null) return false; - if (t == u) return true; - return t.equals(u); + return keyEqual(t, u, myStrategy); } public int hashCode() { diff --git a/platform/util/src/com/intellij/util/containers/WeakHashMap.java b/platform/util/src/com/intellij/util/containers/WeakHashMap.java index 29ba9a6f92a1..14f44e10e7a0 100644 --- a/platform/util/src/com/intellij/util/containers/WeakHashMap.java +++ b/platform/util/src/com/intellij/util/containers/WeakHashMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -15,7 +15,7 @@ */ package com.intellij.util.containers; -import com.intellij.openapi.util.Comparing; +import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -23,6 +23,11 @@ import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.Map; +/** + * Weak keys hash map. + * Null keys are NOT allowed + * Null values are allowed + */ public final class WeakHashMap extends RefHashMap { public WeakHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); @@ -33,33 +38,42 @@ public final class WeakHashMap extends RefHashMap { } public WeakHashMap() { - super(); } public WeakHashMap(@NotNull Map t) { super(t); } + public WeakHashMap(int initialCapacity, float loadFactor, @NotNull TObjectHashingStrategy strategy) { + super(initialCapacity, loadFactor, strategy); + } + + public WeakHashMap(@NotNull TObjectHashingStrategy hashingStrategy) { + super(hashingStrategy); + } + @NotNull @Override - protected Key createKey(@NotNull T k, @NotNull ReferenceQueue q) { - return new WeakKey(k, q); + protected Key createKey(@NotNull T k, @NotNull TObjectHashingStrategy strategy, @NotNull ReferenceQueue q) { + return new WeakKey(k, strategy, q); } 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 + @NotNull private final TObjectHashingStrategy myStrategy; - private WeakKey(@NotNull T k, @NotNull ReferenceQueue q) { + private WeakKey(@NotNull T k, @NotNull TObjectHashingStrategy strategy, @NotNull ReferenceQueue q) { super(k, q); - myHash = k.hashCode(); + myStrategy = strategy; + myHash = strategy.computeHashCode(k); } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Key)) return false; - Object t = get(); - Object u = ((Key)o).get(); - return myHash == o.hashCode() && Comparing.equal(t, u); + T t = get(); + T u = ((Key)o).get(); + return keyEqual(t,u,myStrategy); } public int hashCode() {