weak/soft hash map should work with identity strategy

This commit is contained in:
Alexey Kudravtsev
2015-10-28 14:55:41 +03:00
parent f9547c2ece
commit 87bd01a8ce
4 changed files with 205 additions and 105 deletions
@@ -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<Object, Object> map = new ConcurrentWeakHashMap<Object, Object>(TObjectHashingStrategy.IDENTITY);
public void testConcurrentWeakKeysRemovedWhenIdentityStrategyIsUsed() {
ConcurrentWeakHashMap<Object, Object> 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<Object, Object> map = new WeakHashMap<>(10,0.5f,ContainerUtil.identityStrategy());
Ref<Object> key = Ref.create(new Object());
Ref<Object> 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<Object, Object> map = new ConcurrentSoftHashMap<Object, Object>();
ConcurrentSoftHashMap<Object, Object> map = new ConcurrentSoftHashMap<>();
map.put(this, this);
Set<Map.Entry<Object, Object>> entries = map.entrySet();
assertEquals(1, entries.size());
@@ -68,7 +92,7 @@ public class ConcurrentMapsTest {
@Test(timeout = TIMEOUT)
public void testRemoveFromWeakEntrySet() {
ConcurrentWeakHashMap<Object, Object> map = new ConcurrentWeakHashMap<Object, Object>();
ConcurrentWeakHashMap<Object, Object> map = new ConcurrentWeakHashMap<>();
map.put(this, this);
Set<Map.Entry<Object, Object>> entries = map.entrySet();
assertEquals(1, entries.size());
@@ -79,8 +103,8 @@ public class ConcurrentMapsTest {
}
@Test(timeout = TIMEOUT)
public void testTossedWeakKeysAreRemoved() {
ConcurrentWeakHashMap<Object, Object> map = new ConcurrentWeakHashMap<Object, Object>();
public void testConcurrentWeakTossedWeakKeysAreRemoved() {
ConcurrentWeakHashMap<Object, Object> map = new ConcurrentWeakHashMap<>();
map.put(new Object(), new Object());
do {
@@ -98,8 +122,8 @@ public class ConcurrentMapsTest {
}
@Test(timeout = TIMEOUT)
public void testTossedSoftKeysAreRemoved() {
ConcurrentSoftHashMap<Object, Object> map = new ConcurrentSoftHashMap<Object, Object>();
public void testConcurrentSoftTossedSoftKeysAreRemoved() {
ConcurrentSoftHashMap<Object, Object> 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<Object, Object> map =
(ConcurrentWeakValueHashMap<Object, Object>)ContainerUtil.createConcurrentWeakValueMap();
map.put(new Object(), new Object());
@@ -129,8 +153,8 @@ public class ConcurrentMapsTest {
}
@Test(timeout = TIMEOUT)
public void testTossedSoftValueIsRemoved() {
ConcurrentSoftValueHashMap<Object, Object> map = new ConcurrentSoftValueHashMap<Object, Object>();
public void testConcurrentSoftTossedSoftValueIsRemoved() {
ConcurrentSoftValueHashMap<Object, Object> map = new ConcurrentSoftValueHashMap<>();
map.put(new Object(), new Object());
do {
@@ -144,8 +168,8 @@ public class ConcurrentMapsTest {
}
@Test(timeout = TIMEOUT)
public void testCustomStrategy() {
SoftHashMap<String, String> map = new SoftHashMap<String, String>(CUSTOM_STRATEGY);
public void testSoftMapCustomStrategy() {
SoftHashMap<String, String> 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<String, String> map = new ConcurrentSoftHashMap<String, String>(CUSTOM_STRATEGY);
public void testWeakMapCustomStrategy() {
WeakHashMap<String, String> 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<Object, Object> map = new WeakHashMap<>(ContainerUtil.identityStrategy());
checkHashCodeDoesntCalledFor(map);
}
@Test(timeout = TIMEOUT)
public void testSoftNativeHashCodeDoesNotGetCalledWhenCustomStrategyIsSpecified() {
Map<Object, Object> map = new SoftHashMap<>(ContainerUtil.identityStrategy());
checkHashCodeDoesntCalledFor(map);
}
private void checkHashCodeDoesntCalledFor(Map<Object, Object> 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<String, String> 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<String, String> map = new ConcurrentSoftHashMap<String, String>();
public void testConcurrentSoftNullKey() {
Map<String, String> map = new ConcurrentSoftHashMap<>();
checkNullKeys(map);
}
@Test
public void testNullKeyForConcurrentWeak() {
Map<String, String> map = new ConcurrentWeakHashMap<String, String>();
public void testConcurrentWeakNullKey() {
Map<String, String> map = new ConcurrentWeakHashMap<>();
checkNullKeys(map);
}
@Test(expected = IllegalArgumentException.class)
public void testNullKeyForConcurrentWeakSoft() {
Map<String, String> map = new ConcurrentWeakKeySoftValueHashMap<String, String>(1, 1, 1, CUSTOM_STRATEGY);
public void testConcurrentWeakSoftNullKey() {
Map<String, String> map = new ConcurrentWeakKeySoftValueHashMap<>(1, 1, 1, CUSTOM_STRATEGY);
checkNullKeys(map);
}
@Test(expected = IllegalArgumentException.class)
public void testNullKeyForConcurrentWeakWeak() {
Map<String, String> map = new ConcurrentWeakKeyWeakValueHashMap<String, String>(1, 1, 1, CUSTOM_STRATEGY);
public void testConcurrentWeakWeakNullKey() {
Map<String, String> map = new ConcurrentWeakKeyWeakValueHashMap<>(1, 1, 1, CUSTOM_STRATEGY);
checkNullKeys(map);
}
@@ -203,8 +271,8 @@ public class ConcurrentMapsTest {
}
@Test(timeout = TIMEOUT)
public void testCustomStrategyForConcurrentWeakSoft() {
ConcurrentWeakKeySoftValueHashMap<String, String> map = new ConcurrentWeakKeySoftValueHashMap<String, String>(1, 1, 1, CUSTOM_STRATEGY);
public void testConcurrentWeakSoftCustomStrategy() {
ConcurrentWeakKeySoftValueHashMap<String, String> 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<Object, Object> map = new SoftKeySoftValueHashMap<Object, Object>();
public void testSoftKeySoftValueTossedSoftKeyAndValue() {
SoftKeySoftValueHashMap<Object, Object> map = new SoftKeySoftValueHashMap<>();
map.put(new Object(), new Object());
do {
@@ -228,8 +296,8 @@ public class ConcurrentMapsTest {
}
@Test(timeout = TIMEOUT)
public void testTossedWeakKeyAndValue() {
WeakKeyWeakValueHashMap<Object, Object> map = new WeakKeyWeakValueHashMap<Object, Object>();
public void testWeakKeyWeakValueTossedWeakKeyAndValue() {
WeakKeyWeakValueHashMap<Object, Object> map = new WeakKeyWeakValueHashMap<>();
map.put(new Object(), new Object());
do {
@@ -242,7 +310,7 @@ public class ConcurrentMapsTest {
@Test(timeout = TIMEOUT)
public void testConcurrentLongObjectHashMap() {
ConcurrentLongObjectMap<Object> map = new ConcurrentLongObjectHashMap<Object>();
ConcurrentLongObjectMap<Object> 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<Object> map = new StripedLockIntObjectConcurrentHashMap<Object>();
ConcurrentIntObjectMap<Object> 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<Object> map = new ConcurrentIntObjectHashMap<Object>();
ConcurrentIntObjectMap<Object> 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<Object, Object> map =
(ConcurrentWeakKeyWeakValueHashMap<Object, Object>)ContainerUtil.createConcurrentWeakKeyWeakValueMap();
map.put(new Object(), new Object());
@@ -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<K, V> extends AbstractMap<K, V> implements Map<K, V> {
private final MyMap myMap;
private final ReferenceQueue<K> myReferenceQueue = new ReferenceQueue<K>();
private final HardKey<K> myHardKeyInstance = new HardKey<K>(); // "singleton"
private Set<Entry<K, V>> entrySet = null;
private final HardKey myHardKeyInstance = new HardKey(); // "singleton"
@NotNull
private final TObjectHashingStrategy<K> myStrategy;
private Set<Entry<K, V>> entrySet;
private boolean processingQueue;
public RefHashMap(int initialCapacity, float loadFactor, @NotNull TObjectHashingStrategy<Key<K>> strategy) {
myMap = new MyMap(initialCapacity, loadFactor, strategy);
RefHashMap(int initialCapacity, float loadFactor, @NotNull final TObjectHashingStrategy<K> strategy) {
myStrategy = strategy;
myMap = new MyMap(initialCapacity, loadFactor);
}
public RefHashMap(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, ContainerUtil.<Key<K>>canonicalStrategy());
RefHashMap(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, ContainerUtil.<K>canonicalStrategy());
}
public RefHashMap(int initialCapacity) {
RefHashMap(int initialCapacity) {
this(initialCapacity, 0.8f);
}
public RefHashMap() {
RefHashMap() {
this(4);
}
public RefHashMap(@NotNull Map<K, V> t) {
RefHashMap(@NotNull Map<K, V> t) {
this(Math.max(2 * t.size(), 11), 0.75f);
putAll(t);
}
public RefHashMap(@NotNull final TObjectHashingStrategy<K> hashingStrategy) {
this(4, 0.8f, new TObjectHashingStrategy<Key<K>>() {
@Override
public int computeHashCode(final Key<K> object) {
return hashingStrategy.computeHashCode(object.get());
}
RefHashMap(@NotNull final TObjectHashingStrategy<K> hashingStrategy) {
this(4, 0.8f, hashingStrategy);
}
@Override
public boolean equals(final Key<K> o1, final Key<K> o2) {
return hashingStrategy.equals(o1.get(), o2.get());
}
});
static <K> boolean keyEqual(K k1, K k2, TObjectHashingStrategy<K> strategy) {
return k1 == k2 || strategy.equals(k1, k2);
}
private class MyMap extends THashMap<Key<K>, V> {
private MyMap(int initialCapacity, float loadFactor, @NotNull TObjectHashingStrategy<Key<K>> strategy) {
super(initialCapacity, loadFactor, strategy);
private MyMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, new TObjectHashingStrategy<Key<K>>() {
@Override
public int computeHashCode(final Key<K> key) {
return key.hashCode(); // use stored hashCode
}
@Override
public boolean equals(final Key<K> o1, final Key<K> o2) {
return o1 == o2 || keyEqual(o1.get(), o2.get(), myStrategy);
}
});
}
@Override
@@ -123,33 +131,32 @@ abstract class RefHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
}
@NotNull
protected abstract <T> Key<T> createKey(@NotNull T k, @NotNull ReferenceQueue<? super T> q);
protected abstract <T> Key<T> createKey(@NotNull T k, @NotNull TObjectHashingStrategy<T> strategy, @NotNull ReferenceQueue<? super T> q);
private static class HardKey<T> implements Key<T> {
private T myObject;
private class HardKey implements Key<K> {
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<T>)o).get();
return t == u || t.equals(u);
K t = myObject;
K u = ((Key<K>)o).get();
return keyEqual(t, u, myStrategy);
}
public int hashCode() {
@@ -164,7 +171,7 @@ abstract class RefHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
processingQueue = true;
Key<K> wk;
while ((wk = (Key<K>)myReferenceQueue.poll()) != null) {
myMap.remove(wk);
removeKey(wk);
processed = true;
}
}
@@ -181,7 +188,7 @@ abstract class RefHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
@NotNull
Key<K> createKey(@NotNull K key) {
return createKey(key, myReferenceQueue);
return createKey(key, myStrategy, myReferenceQueue);
}
V putKey(@NotNull Key<K> weakKey, V value) {
@@ -242,13 +249,15 @@ abstract class RefHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
private static class MyEntry<K, V> implements Entry<K, V> {
private final 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 */
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<K> myStrategy;
private MyEntry(@NotNull Entry<?, V> ent, K key) {
private MyEntry(@NotNull Entry<?, V> ent, @NotNull K key, int keyHashCode, @NotNull TObjectHashingStrategy<K> strategy) {
this.ent = ent;
this.key = key;
myKeyHashCode = keyHashCode;
myStrategy = strategy;
}
@Override
@@ -266,20 +275,19 @@ abstract class RefHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
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<K,V> 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<K, V> extends AbstractMap<K, V> implements Map<K, V> {
public Iterator<Entry<K, V>> iterator() {
return new Iterator<Entry<K, V>>() {
private final Iterator<Entry<Key<K>, V>> hashIterator = hashEntrySet.iterator();
private MyEntry<K, V> next = null;
private MyEntry<K, V> next;
@Override
public boolean hasNext() {
while (hashIterator.hasNext()) {
Entry<Key<K>, V> ent = hashIterator.next();
Key<K> 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<K, V>(ent, k);
next = new MyEntry<K, V>(ent, k, wk.hashCode(), myStrategy);
return true;
}
return false;
@@ -361,8 +369,8 @@ abstract class RefHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
public int hashCode() {
int h = 0;
for (Entry entry : hashEntrySet) {
Key wk = (Key)entry.getKey();
for (Entry<Key<K>,V> entry : hashEntrySet) {
Key<K> 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<K, V> extends AbstractMap<K, V> implements Map<K, V> {
if (entrySet == null) entrySet = new EntrySet();
return entrySet;
}
@TestOnly
int underlyingMapSize() {
return myMap.size();
}
}
@@ -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<K,V> extends RefHashMap<K,V> {
public SoftHashMap(int initialCapacity, float loadFactor) {
@@ -37,7 +38,6 @@ public final class SoftHashMap<K,V> extends RefHashMap<K,V> {
}
public SoftHashMap() {
super();
}
public SoftHashMap(@NotNull Map<K, V> t) {
@@ -48,29 +48,34 @@ public final class SoftHashMap<K,V> extends RefHashMap<K,V> {
super(hashingStrategy);
}
public SoftHashMap(int initialCapacity, float loadFactor, @NotNull TObjectHashingStrategy<K> strategy) {
super(initialCapacity, loadFactor, strategy);
}
@NotNull
@Override
protected <T> Key<T> createKey(@NotNull T k, @NotNull ReferenceQueue<? super T> q) {
return new SoftKey<T>(k, q);
protected <T> Key<T> createKey(@NotNull T k, @NotNull TObjectHashingStrategy<T> strategy, @NotNull ReferenceQueue<? super T> q) {
return new SoftKey<T>(k, strategy, q);
}
private static class SoftKey<T> extends SoftReference<T> implements Key<T> {
private final int myHash; /* Hash code of key, stored here since the key may be tossed by the GC */
@NotNull private final TObjectHashingStrategy<T> myStrategy;
private SoftKey(@NotNull T k, @NotNull ReferenceQueue<? super T> q) {
private SoftKey(@NotNull T k, @NotNull TObjectHashingStrategy<T> strategy, @NotNull ReferenceQueue<? super T> 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<T>)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() {
@@ -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<K, V> extends RefHashMap<K, V> {
public WeakHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
@@ -33,33 +38,42 @@ public final class WeakHashMap<K, V> extends RefHashMap<K, V> {
}
public WeakHashMap() {
super();
}
public WeakHashMap(@NotNull Map<K, V> t) {
super(t);
}
public WeakHashMap(int initialCapacity, float loadFactor, @NotNull TObjectHashingStrategy<K> strategy) {
super(initialCapacity, loadFactor, strategy);
}
public WeakHashMap(@NotNull TObjectHashingStrategy<K> hashingStrategy) {
super(hashingStrategy);
}
@NotNull
@Override
protected <T> Key<T> createKey(@NotNull T k, @NotNull ReferenceQueue<? super T> q) {
return new WeakKey<T>(k, q);
protected <T> Key<T> createKey(@NotNull T k, @NotNull TObjectHashingStrategy<T> strategy, @NotNull ReferenceQueue<? super T> q) {
return new WeakKey<T>(k, strategy, q);
}
private static class WeakKey<T> extends WeakReference<T> implements Key<T> {
private final int myHash; // Hashcode of key, stored here since the key may be tossed by the GC
@NotNull private final TObjectHashingStrategy<T> myStrategy;
private WeakKey(@NotNull T k, @NotNull ReferenceQueue<? super T> q) {
private WeakKey(@NotNull T k, @NotNull TObjectHashingStrategy<T> strategy, @NotNull ReferenceQueue<? super T> 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<T>)o).get();
return keyEqual(t,u,myStrategy);
}
public int hashCode() {