mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
weak/soft hash map should work with identity strategy
This commit is contained in:
+101
-33
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user