コード例 #1
0
  /**
   * Returns the value for {@code key} if it exists in the cache or can be created by {@code
   * #create}. If a value was returned, it is moved to the head of the queue. This returns null if a
   * value is not cached and cannot be created.
   */
  public final V get(K key) {
    if (key == null) {
      throw new NullPointerException("key == null");
    }

    V mapValue;
    synchronized (this) {
      // If expired, remove the entry.
      if (!keyExpiryMap.containsKey(key)) {
        this.remove(key);
        return null;
      }
      mapValue = map.get(key);
      if (mapValue != null) {
        hitCount++;
        return mapValue;
      }
      missCount++;
    }

    /*
     * Attempt to create a value. This may take a long time, and the map
     * may be different when create() returns. If a conflicting value was
     * added to the map while create() was working, we leave that value in
     * the map and release the created value.
     */

    V createdValue = create(key);
    if (createdValue == null) {
      return null;
    }

    synchronized (this) {
      createCount++;
      mapValue = map.put(key, createdValue);

      if (mapValue != null) {
        // There was a conflict so undo that last put
        map.put(key, mapValue);
      } else {
        size += safeSizeOf(key, createdValue);
      }
    }

    if (mapValue != null) {
      entryRemoved(false, key, createdValue, mapValue);
      return mapValue;
    } else {
      trimToSize(maxSize);
      return createdValue;
    }
  }
コード例 #2
0
  /**
   * Removes the entry for {@code key} if it exists.
   *
   * @return the previous value mapped by {@code key}.
   */
  public final V remove(K key) {
    if (key == null) {
      throw new NullPointerException("key == null");
    }

    V previous;
    synchronized (this) {
      previous = map.remove(key);
      keyExpiryMap.remove(key);
      if (previous != null) {
        size -= safeSizeOf(key, previous);
      }
    }

    if (previous != null) {
      entryRemoved(false, key, previous, null);
    }

    return previous;
  }
コード例 #3
0
  /**
   * @param maxSize the maximum size of the cache before returning. May be -1 to evict even 0-sized
   *     elements.
   */
  private void trimToSize(int maxSize) {
    while (true) {
      K key;
      V value;
      synchronized (this) {
        if (size <= maxSize || map.isEmpty()) {
          break;
        }

        Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
        key = toEvict.getKey();
        value = toEvict.getValue();
        map.remove(key);
        keyExpiryMap.remove(key);
        size -= safeSizeOf(key, value);
        evictionCount++;
      }

      entryRemoved(true, key, value, null);
    }
  }
コード例 #4
0
  /**
   * Caches {@code value} for {@code key}. The value is moved to the head of the queue.
   *
   * @return the previous value mapped by {@code key}.
   */
  public final V put(K key, V value, long expiryTimestamp) {
    if (key == null || value == null) {
      throw new NullPointerException("key == null || value == null");
    }

    V previous;
    synchronized (this) {
      putCount++;
      size += safeSizeOf(key, value);
      previous = map.put(key, value);
      keyExpiryMap.put(key, expiryTimestamp);
      if (previous != null) {
        size -= safeSizeOf(key, previous);
      }
    }

    if (previous != null) {
      entryRemoved(false, key, previous, value);
    }

    trimToSize(maxSize);
    return previous;
  }
コード例 #5
0
 /** Clear the cache, calling {@link #entryRemoved} on each removed entry. */
 public final void evictAll() {
   trimToSize(-1); // -1 will evict 0-sized elements
   keyExpiryMap.clear();
 }