com.intellij.util.containers.ConcurrentWeakKeySoftValueHashMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of util Show documentation
Show all versions of util Show documentation
A packaging of the IntelliJ Community Edition util library.
This is release number 1 of trunk branch 142.
The newest version!
/*
* 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.
* 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 com.intellij.openapi.util.Getter;
import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import static com.intellij.reference.SoftReference.deref;
/**
* Concurrent map with weak keys and soft values.
* Null keys are NOT allowed
* Null values are NOT allowed
* @deprecated Use {@link ContainerUtil#createConcurrentWeakKeySoftValueMap(int, float, int, gnu.trove.TObjectHashingStrategy)} instead
*/
public class ConcurrentWeakKeySoftValueHashMap implements ConcurrentMap {
private final ConcurrentMap, ValueReference> myMap;
final ReferenceQueue myKeyQueue = new ReferenceQueue();
final ReferenceQueue myValueQueue = new ReferenceQueue();
@NotNull final TObjectHashingStrategy myHashingStrategy;
public ConcurrentWeakKeySoftValueHashMap(int initialCapacity,
float loadFactor,
int concurrencyLevel,
@NotNull final TObjectHashingStrategy hashingStrategy) {
myHashingStrategy = hashingStrategy;
myMap = ContainerUtil.newConcurrentMap(initialCapacity, loadFactor, concurrencyLevel, ContainerUtil.>canonicalStrategy());
}
public interface KeyReference extends Getter {
@Override
K get();
@NotNull
ValueReference getValueReference(); // no strong references
// MUST work even with gced references for the code in processQueue to work
@Override
boolean equals(Object o);
@Override
int hashCode();
}
public interface ValueReference extends Getter {
@NotNull
KeyReference getKeyReference(); // no strong references
@Override
V get();
}
static class WeakKey extends WeakReference implements KeyReference {
private final int myHash; // Hash code of the key, stored here since the key may be tossed by the GC
private final TObjectHashingStrategy myStrategy;
@NotNull private final ValueReference myValueReference;
WeakKey(@NotNull K k,
@NotNull ValueReference valueReference,
@NotNull TObjectHashingStrategy strategy,
@NotNull ReferenceQueue queue) {
super(k, queue);
myValueReference = valueReference;
myHash = strategy.computeHashCode(k);
myStrategy = strategy;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof KeyReference)) return false;
K t = get();
K other = ((KeyReference)o).get();
if (t == null || other == null) return false;
if (t == other) return true;
return myHash == o.hashCode() && myStrategy.equals(t, other);
}
@Override
public int hashCode() {
return myHash;
}
@NotNull
@Override
public ValueReference getValueReference() {
return myValueReference;
}
}
private static class SoftValue extends SoftReference implements ValueReference {
@NotNull private volatile KeyReference myKeyReference; // can't make it final because of circular dependency of KeyReference to ValueReference
private SoftValue(@NotNull V value, @NotNull ReferenceQueue queue) {
super(value, queue);
}
// When referent is collected, equality should be identity-based (for the processQueues() remove this very same SoftValue)
// otherwise it's just canonical equals on referents for replace(K,V,V) to work
@Override
public final boolean equals(final Object o) {
if (this == o) return true;
if (o == null) return false;
V v = get();
Object thatV = ((ValueReference)o).get();
return v != null && thatV != null && v.equals(thatV);
}
@NotNull
@Override
public KeyReference getKeyReference() {
return myKeyReference;
}
}
@NotNull
protected KeyReference createKeyReference(@NotNull K k, @NotNull final V v) {
final ValueReference valueReference = createValueReference(v, myValueQueue);
WeakKey keyReference = new WeakKey(k, valueReference, myHashingStrategy, myKeyQueue);
if (valueReference instanceof SoftValue) {
((SoftValue)valueReference).myKeyReference = keyReference;
}
return keyReference;
}
@NotNull
protected ValueReference createValueReference(@NotNull V value, @NotNull ReferenceQueue queue) {
return new SoftValue(value, queue);
}
@Override
public int size() {
return myMap.size();
}
@Override
public boolean isEmpty() {
return myMap.isEmpty();
}
@Override
public void clear() {
processQueues();
myMap.clear();
}
/////////////////////////////
private static class HardKey implements KeyReference {
private K myKey;
private int myHash;
private void set(@NotNull K key, int hash) {
myKey = key;
myHash = hash;
}
private void clear() {
myKey = null;
}
@Override
public K get() {
return myKey;
}
@Override
public boolean equals(Object o) {
return o.equals(this); // see WeakKey.equals()
}
@Override
public int hashCode() {
return myHash;
}
@NotNull
@Override
public ValueReference getValueReference() {
throw new UnsupportedOperationException();
}
}
private static final ThreadLocal HARD_KEY = new ThreadLocal() {
@Override
protected HardKey initialValue() {
return new HardKey();
}
};
@NotNull
private HardKey createHardKey(Object o) {
@SuppressWarnings("unchecked") K key = (K)o;
@SuppressWarnings("unchecked") HardKey hardKey = HARD_KEY.get();
hardKey.set(key, myHashingStrategy.computeHashCode(key));
return hardKey;
}
///////////////////////////////////
@Override
public V get(@NotNull Object key) {
HardKey hardKey = createHardKey(key);
ValueReference valueReference = myMap.get(hardKey);
V v = deref(valueReference);
hardKey.clear();
return v;
}
@Override
public boolean containsKey(Object key) {
HardKey hardKey = createHardKey(key);
boolean result = myMap.containsKey(hardKey);
hardKey.clear();
return result;
}
@Override
public boolean containsValue(Object value) {
throw new UnsupportedOperationException();
}
@Override
public V remove(Object key) {
processQueues();
HardKey hardKey = createHardKey(key);
ValueReference valueReference = myMap.remove(hardKey);
V v = deref(valueReference);
hardKey.clear();
return v;
}
@Override
public void putAll(@NotNull Map m) {
for (Map.Entry e : m.entrySet()) {
put(e.getKey(), e.getValue());
}
}
@Override
public V put(K key, V value) {
processQueues();
KeyReference keyReference = createKeyReference(key, value);
ValueReference valueReference = keyReference.getValueReference();
ValueReference prevValReference = myMap.put(keyReference, valueReference);
return deref(prevValReference);
}
boolean processQueues() {
boolean removed = false;
KeyReference keyReference;
while ((keyReference = (KeyReference)myKeyQueue.poll()) != null) {
ValueReference valueReference = keyReference.getValueReference();
removed |= myMap.remove(keyReference, valueReference);
}
ValueReference valueReference;
while ((valueReference = (ValueReference)myValueQueue.poll()) != null) {
keyReference = valueReference.getKeyReference();
removed |= myMap.remove(keyReference, valueReference);
}
return removed;
}
@NotNull
@Override
public Set keySet() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Collection values() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Set> entrySet() {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(@NotNull Object key, @NotNull Object value) {
processQueues();
HardKey hardKey = createHardKey(key);
ValueReference valueReference = myMap.get(hardKey);
V v = deref(valueReference);
boolean result = value.equals(v) && myMap.remove(hardKey, valueReference);
hardKey.clear();
return result;
}
@Override
public V putIfAbsent(@NotNull K key, @NotNull V value) {
processQueues();
KeyReference keyReference = createKeyReference(key, value);
ValueReference valueReference = keyReference.getValueReference();
ValueReference result = myMap.putIfAbsent(keyReference, valueReference);
return deref(result);
}
@Override
public boolean replace(@NotNull K key, @NotNull V oldValue, @NotNull V newValue) {
processQueues();
KeyReference oldKeyReference = createKeyReference(key, oldValue);
ValueReference oldValueReference = oldKeyReference.getValueReference();
KeyReference newKeyReference = createKeyReference(key, newValue);
ValueReference newValueReference = newKeyReference.getValueReference();
return myMap.replace(oldKeyReference, oldValueReference, newValueReference);
}
@Override
public V replace(@NotNull K key, @NotNull V value) {
processQueues();
KeyReference keyReference = createKeyReference(key, value);
ValueReference valueReference = keyReference.getValueReference();
ValueReference result = myMap.replace(keyReference, valueReference);
return deref(result);
}
}