All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intellij.util.containers.ConcurrentWeakKeySoftValueHashMap Maven / Gradle / Ivy

Go to download

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);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy