Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2012 The Guava Authors
   *
   * 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.
  */
 
 package com.google.common.cache;
 
 import static com.google.common.base.Objects.firstNonNull;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 
 
 import java.util.Map;
 import java.util.Set;
 
LocalCache emulation for GWT.

Parameters:
<K> the base key type
<V> the base value type
Author(s):
Charles Fry
Jon Donovan
 
 public class LocalCache<K, V> implements ConcurrentMap<K, V> {
   private static final int UNSET_INT = .;
   
   private final LinkedHashMap<K, Timestamped<V>> cachingHashMap;
   private final CacheLoader<? super K, V> loader;
   private final RemovalListener removalListener;
   private final StatsCounter statsCounter;
   private final Ticker ticker;
   private final long expireAfterWrite;
   private final long expireAfterAccess;
 
   LocalCache(CacheBuilder<? super K, ? super V> builderCacheLoader<? super K, V> loader) {
     this. = loader;   
     this. = builder.removalListener;
     this. = builder.expireAfterAccessNanos;
     this. = builder.expireAfterWriteNanos;
     this. = builder.getStatsCounterSupplier().get();
     
     /* Implements size-capped LinkedHashMap */
     final long maximumSize = builder.maximumSize;
     this. = new CapacityEnforcingLinkedHashMap<K, V>(
         builder.getInitialCapacity(),
         0.75f,
         (builder.maximumSize != ),
         builder.maximumSize,
         ,
         );
 
     this. = firstNonNull(builder.ticker, Ticker.systemTicker());
   }
   
   @Override
   public int size() {
     return .size();
   }
 
   @Override
   public boolean isEmpty() {
     return .isEmpty();
   }
   
   @Override
   public V get(Object key) {
     key = checkNotNull(key);
     Timestamped<V> value = .get(key);
 
     if (value == null) {
      return null;
    } else if (!isExpired(value)) {
      .recordHits(1);
      value.updateTimestamp();
      return value.getValue();
    } else {
      .remove(key);
      return null;
    }
  }
  public V put(K key, V value) {
    key = checkNotNull(key);
    value = checkNotNull(value);
    Timestamped<V> oldValue = .put(keynew Timestamped<V>(value));
    if (oldValue == null) {
      return null;
    }
    return oldValue.getValue();
  }
  public V remove(Object key) {
    Timestamped<V> stamped = .remove(key);
    if (stamped != null) {
      V value = stamped.getValue();
      
      if (!isExpired(stamped)) {
        alertListenerIfPresent(keyvalue.);
        return value;
      }
  
      alertListenerIfPresent(keyvalue.);
    }
    return null;
  }
  public void putAll(Map<? extends K, ? extends V> m) {
    for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
      put(entry.getKey(), entry.getValue());
    }
  }
  public void clear() {
    if ( != null) {
      for (Map.Entry<K, Timestamped<V>> entry : .entrySet()) {
        alertListenerIfPresent(entry.getKey(), entry.getValue().getValue(), .);
      }
    }
  }
  public V putIfAbsent(K key, V value) {
    V currentValue = get(key);
    if (currentValue != null) {
      return currentValue;
    }
    return put(keyvalue);
  }
  public boolean remove(Object keyObject value) {
    if (value.equals(get(key))) {
      alertListenerIfPresent(keyvalue.);
      remove(key);
      return true;
    }
    return false;
  }
  public boolean replace(K key, V oldValue, V newValue) {
    if (oldValue.equals(get(key))) {
      alertListenerIfPresent(keyoldValue.);
      put(keynewValue);
      return true;
    }
    return false;
  }
  public V replace(K key, V value) {
    V currentValue = get(key);
    if (currentValue != null) {
      alertListenerIfPresent(keycurrentValue.);
      return put(keyvalue);
    }
    return null;
  }
  
  public boolean containsKey(Object key) {
    return .containsKey(key) && !isExpired(.get(key));
  }
  
  public boolean containsValue(Object value) {
    for (Timestamped<V> val : .values()) {
      if (val.getValue().equals(value)) {
        if (!isExpired(val)) {
          return true;
        }
      }
    }
    return false;
  }
  private boolean isExpired(Timestamped<V> stamped) {
    if (( == ) && ( == )) {
      return false;
    }
    boolean expireWrite = (stamped.getWriteTimestamp() +  <= currentTimeNanos());
    boolean expireAccess = (stamped.getAccessTimestamp() +  <= currentTimeNanos());
    
    if ( == ) {
      return expireWrite;
    }
    if ( == ) {
      return expireAccess;
    }
    return expireWrite || expireAccess;
  }
  private long currentTimeNanos() {
    return .read();
  }
  
  private void alertListenerIfPresent(Object keyObject valueRemovalCause cause) {
    if ( != null) {
      .onRemoval(new RemovalNotification(keyvaluecause));
    }
  }
  private V load(Object keythrows ExecutionException {
    long startTime = .read();
    V calculatedValue;
    try {
      /*
       * This cast isn't safe, but we can rely on the fact that K is almost always passed to
       * Map.get(), and tools like IDEs and Findbugs can catch situations where this isn't the
       * case.
       *
       * The alternative is to add an overloaded method, but the chances of a user calling get()
       * instead of the new API and the risks inherent in adding a new API outweigh this little
       * hole.
       */
      K castKey = (K) key;
      calculatedValue = .load(castKey);
      put(castKeycalculatedValue);
    } catch (RuntimeException e) {
      .recordLoadException(.read() - startTime);
      throw new UncheckedExecutionException(e);
    } catch (Exception e) {
      .recordLoadException(.read() - startTime);
      throw new ExecutionException(e);
    } catch (Error e) {
      .recordLoadException(.read() - startTime);
      throw new ExecutionError(e);
    }
    if (calculatedValue == null) {
      String message =  + " returned null for key " + key + ".";
      throw new CacheLoader.InvalidCacheLoadException(message);
    }
    .recordLoadSuccess(.read() - startTime);
    return calculatedValue;
  }
  
  private V getIfPresent(Object key) {
    key = checkNotNull(key);
    Timestamped<V> value = .get(key);
    if (value == null) {
      return null;
    } else if (!isExpired(value)) {
      value.updateTimestamp();
      return value.getValue();
    } else {
      .remove(key);
      return null;
    }    
  }
  private V getOrLoad(K keythrows ExecutionException{
    V value = get(key);
    if (value != null) {
      return value;
    }
    return load(key);
  }
  
  private static class Timestamped<V> {
    private final V value;
    private final Ticker ticker;
    private long writeTimestamp;
    private long accessTimestamp;
    
    public Timestamped(V valueTicker ticker) {
      this. = checkNotNull(value);
      this. = checkNotNull(ticker);
      this. = ticker.read();
      this. = this.;
    }
    
    public V getValue() {
      return ;
    }
    
    public void updateTimestamp() {
       = .read();
    }
  
    public long getAccessTimestamp() {
      return ;
    }
    
    public long getWriteTimestamp() {
      return ;
    }
    
    public boolean equals(Object o) {
      return .equals(o);
    }
    
    public int hashCode() {
      return .hashCode();
    }
  }

  
LocalManualCache is a wrapper around LocalCache for a cache without loading.

Parameters:
<K> the base key type
<V> the base value type
  public static class LocalManualCache<K, V> extends AbstractCache<K, V> {
    final LocalCache<K, V> localCache;
    LocalManualCache(CacheBuilder<? super K, ? super V> builder) {
      this(buildernull);
    }
    protected LocalManualCache(CacheBuilder<? super K, ? super V> builder,
        CacheLoader<? super K, V> loader) {
      this. = new LocalCache<K, V>(builderloader);
    }
    // Cache methods
    @Override
    public V get(K keyCallable<? extends V> valueLoaderthrows ExecutionException {
      V value = .get(key);
      if (value != null) {
        return value;
      }
      
      try {
        V newValue = valueLoader.call();
        .put(keynewValue);
        return newValue;
      } catch (Exception e) {
        throw new ExecutionException(e);
      }
    }
    
    @Override
    @Nullable
    public V getIfPresent(Object key) {
      return .getIfPresent(key);
    }
    @Override
    public void put(K key, V value) {
      .put(keyvalue);
    }
    @Override
    public void invalidate(Object key) {
      key = checkNotNull(key);
      .remove(key);
    }
    @Override
    public void invalidateAll() {
      .clear();
    }
    @Override
    public long size() {
      return .size();
    }
    @Override
    public ConcurrentMap<K, V> asMap() {
      return ;
    }
  }

  
LocalLoadingCache is a wrapper around LocalCache for a cache with loading.

Parameters:
<K> the base key type
<V> the base value type
  public static class LocalLoadingCache<K, V>
      extends LocalManualCache<K, V> implements LoadingCache<K, V> {
    LocalLoadingCache(CacheBuilder<? super K, ? super V> builder,
        CacheLoader<? super K, V> loader) {
      super(buildercheckNotNull(loader));
    }
    // Cache methods
    @Override
    public V get(K keythrows ExecutionException {
      return .getOrLoad(key);
    }
    @Override
    public V getUnchecked(K key) {
      try {
        return get(key);
      } catch (ExecutionException e) {
        throw new UncheckedExecutionException(e.getCause());
      }
    }
    @Override
    public final V apply(K key) {
      return getUnchecked(key);
    }
    @Override
    public ImmutableMap<K, V> getAll(Iterable<? extends K> keysthrows ExecutionException {
      Map<K, V> map = new HashMap<K, V>();
      for (K key : keys) {
        map.put(key.getOrLoad(key));
      }
      return ImmutableMap.copyOf(map);
    }
    
    @Override
    public void refresh(K key) {
      throw new UnsupportedOperationException();
    }
  }
  
  
LinkedHashMap that enforces it's maximum size and logs events in a StatsCounter object and an optional RemovalListener.

Parameters:
<K> the base key type
<V> the base value type
  private class CapacityEnforcingLinkedHashMap<K, V> extends LinkedHashMap<K, Timestamped<V>> {
    
    private final StatsCounter statsCounter;
    private final RemovalListener removalListener;
    private final long maximumSize;
    
        int initialCapacity,
        float loadFactor,
        boolean accessOrder,
        long maximumSize,
        StatsCounter statsCounter,
        @Nullable RemovalListener removalListener) {
      super(initialCapacityloadFactoraccessOrder);
      this. = maximumSize;
      this. = statsCounter;
      this. = removalListener;
    }
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, Timestamped<V>> ignored) {
      boolean removal = ( == ) ? false : (size() > );
      if (( != null) && removal) {
            ignored.getKey(), 
            ignored.getValue().getValue(),
            .));
      }
      return removal;
    }
  }
  
  
Any updates to LocalCache.Strength used in CacheBuilder need to be matched in this class for compilation purposes.
  enum Strength {
    /*
     * TODO(kevinb): If we strongly reference the value and aren't loading, we needn't wrap the
     * value. This could save ~8 bytes per entry.
     */
    STRONG {
      @Override
        return Equivalence.equals();
      }
    },
    SOFT {
      @Override
        return Equivalence.identity();
      }
    },
    WEAK {
      @Override
        return Equivalence.identity();
      }
    };
    
  }
  
  

Implementation for the EntryIterator, which is used to build Key and Value iterators.

Expiration is only checked on hasNext(), so as to ensure that a next() call never returns null when hasNext() has already been called.

  class EntryIterator implements Iterator<Entry<K, V>> {
    Entry<K, Timestamped<V>> lastEntry;
    Entry<K, Timestamped<V>> nextEntry;
    EntryIterator() {
      this. = LocalCache.this..entrySet().iterator();
    }
    @Override
    public Entry<K, V> next() {
      if ( == null) {
        hasNext();
        if ( == null) {
          throw new NoSuchElementException();
        }
      }
      
       = ;
       = null;
      return new WriteThroughEntry(.getKey(), .getValue().getValue());
    }
    @Override
    public boolean hasNext() {
      if ( == null) {
        while (.hasNext()) {
          Entry<K, Timestamped<V>> next = .next();
          if (!isExpired(next.getValue())) {
             = next;
            return true;
          }
        }
        return false;
      }
      return true;
    }
    @Override
    public void remove() {
      checkState( != null);
       = null;
    }
  }

  
KeyIterator build on top of EntryIterator.
  final class KeyIterator implements Iterator<K> {
    private EntryIterator iterator;
    
    KeyIterator() {
       = new EntryIterator();
    }
    
    @Override
    public boolean hasNext() {
      return .hasNext();
    }
    @Override
    public K next() {
      return .next().getKey();
    }
    @Override
    public void remove() {
      .remove();
    }
  }

  
ValueIterator build on top of EntryIterator.
  final class ValueIterator implements Iterator<V> {
    private EntryIterator iterator;
    
    ValueIterator() {
       = new EntryIterator();
    }
    
    @Override
    public boolean hasNext() {
      return .hasNext();
    }
    @Override
    public V next() {
      return .next().getValue();
    }
    @Override
    public void remove() {
      .remove();
    }
  }
  Set<K> keySet;
  public Set<K> keySet() {
    // does not impact recency ordering
    Set<K> ks = ;
    return (ks != null) ? ks : ( = new KeySet(this));
  }
  public Collection<V> values() {
    // does not impact recency ordering
    Collection<V> vs = ;
    return (vs != null) ? vs : ( = new Values(this));
  }
  Set<Entry<K, V>> entrySet;
  public Set<Entry<K, V>> entrySet() {
    // does not impact recency ordering
    Set<Entry<K, V>> es = ;
    return (es != null) ? es : ( = new EntrySet(this));
  }
  
  
Custom Entry class used by EntryIterator.next(), that relays setValue changes to the underlying map.
  private final class WriteThroughEntry implements Entry<K, V> {
    final K key;
    V value;
    WriteThroughEntry(K key, V value) {
      this. = checkNotNull(key);
      this. = checkNotNull(value);
    }
    @Override
    public K getKey() {
      return ;
    }
    @Override
    public V getValue() {
      return ;
    }
    @Override
    public boolean equals(@Nullable Object object) {
      // Cannot use key and value equivalence
      if (object instanceof Entry) {
        Entry<?, ?> that = (Entry<?, ?>) object;
        return .equals(that.getKey()) && .equals(that.getValue());
      }
      return false;
    }
    @Override
    public int hashCode() {
      // Cannot use key and value equivalence
      return .hashCode() ^ .hashCode();
    }
    @Override
    public V setValue(V newValue) {
      throw new UnsupportedOperationException();
    }

    
Returns a string representation of the form {key}={value}.
    @Override 
    public String toString() {
      return getKey() + "=" + getValue();
    }
  }
  
  // TODO(fry): Separate logic for consistency between emul and nonemul implementation.
  // TODO(fry): Look into Maps.KeySet and Maps.Values, which can ideally be reused here but are
  // currently only package visible.
  abstract class AbstractCacheSet<T> extends AbstractSet<T> {
    final ConcurrentMap<?, ?> map;
    
    AbstractCacheSet(ConcurrentMap<?, ?> map) {
      this. = map;
    }
    
    @Override
    public int size() {
      return .size();
    }
    
    @Override
    public boolean isEmpty() {
      return .isEmpty();
    }
    
    @Override
    public void clear() {
      .clear();
    }
  }

  
Abstraction layer for the KeySet, which redirects to cache methods.
  private final class KeySet extends AbstractCacheSet<K> {
    KeySet(ConcurrentMap<?, ?> map) {
      super(map);
    }
    @Override
    public Iterator<K> iterator() {
      return new KeyIterator();
    }
    @Override
    public boolean contains(Object o) {
      return .containsKey(o);
    }
    @Override
    public boolean remove(Object o) {
      return .remove(o) != null;
    }
  }
  
  
Abstraction layer for the Values set, which redirects to cache methods.
  private final class Values extends AbstractCacheSet<V> {
    Values(ConcurrentMap<?, ?> map) {
      super(map);
    }
    @Override
    public Iterator<V> iterator() {
      return new ValueIterator();
    }
    @Override
    public boolean contains(Object o) {
      return .containsValue(o);
    }
  }

  
Abstraction layer for the EntrySet, which redirects to cache methods.
  private final class EntrySet extends AbstractCacheSet<Entry<K, V>> {
    EntrySet(ConcurrentMap<?, ?> map) {
      super(map);
    }
    
    @Override
    public Iterator<Entry<K, V>> iterator() {
      return new EntryIterator();
    }
    @Override
    public boolean contains(Object o) {
      if (!(o instanceof Entry)) {
        return false;
      }
      Entry<?, ?> e = (Entry<?, ?>) o;
      Object key = e.getKey();
      if (key == null) {
        return false;
      }
      V v = LocalCache.this.get(key);
      return (v != null) && e.getValue().equals(v);
    }
    @Override
    public boolean remove(Object o) {
      if (!(o instanceof Entry)) {
        return false;
      }
      Entry<?, ?> e = (Entry<?, ?>) o;
      Object key = e.getKey();
      return (key != null) && LocalCache.this.remove(keye.getValue());
    }
  }