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

com.google.common.cache.CacheBuilder Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

The newest version!
/*
 * Copyright (C) 2009 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.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Ascii;
import com.google.common.base.Equivalence;
import com.google.common.base.MoreObjects;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.base.Ticker;
import com.google.common.cache.AbstractCache.SimpleStatsCounter;
import com.google.common.cache.AbstractCache.StatsCounter;
import com.google.common.cache.LocalCache.Strength;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.j2objc.annotations.J2ObjCIncompatible;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ConcurrentModificationException;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;

/**
 * A builder of {@link LoadingCache} and {@link Cache} instances.
 *
 * 

Prefer Caffeine over Guava's caching * API

* *

The successor to Guava's caching API is Caffeine. Its API is designed to make it a * nearly drop-in replacement -- though it requires Java 8 APIs, is not available for Android or * GWT/j2cl, and may have different * (usually better) behavior when multiple threads attempt concurrent mutations. Its equivalent * to {@code CacheBuilder} is its {@code * Caffeine} class. Caffeine offers better performance, more features (including asynchronous * loading), and fewer bugs. * *

Caffeine defines its own interfaces ({@code * Cache}, {@code * LoadingCache}, {@code * CacheLoader}, etc.), so you can use Caffeine without needing to use any Guava types. * Caffeine's types are better than Guava's, especially for their * deep support for asynchronous operations. But if you want to migrate to Caffeine with minimal * code changes, you can use its * {@code CaffeinatedGuava} adapter class, which lets you build a Guava {@code Cache} or a Guava * {@code LoadingCache} backed by a Guava {@code CacheLoader}. * *

Caffeine's API for asynchronous operations uses {@code CompletableFuture}: {@code * AsyncLoadingCache.get} returns a {@code CompletableFuture}, and implementations of {@code * AsyncCacheLoader.asyncLoad} must return a {@code CompletableFuture}. Users of Guava's {@link * com.google.common.util.concurrent.ListenableFuture} can adapt between the two {@code Future} * types by using {@code * net.javacrumbs.futureconverter.java8guava.FutureConverter}. * *

More on {@code CacheBuilder}

* * {@code CacheBuilder} builds caches with any combination of the following features: * *
    *
  • automatic loading of entries into the cache *
  • least-recently-used eviction when a maximum size is exceeded (note that the cache is * divided into segments, each of which does LRU internally) *
  • time-based expiration of entries, measured since last access or last write *
  • keys automatically wrapped in {@code WeakReference} *
  • values automatically wrapped in {@code WeakReference} or {@code SoftReference} *
  • notification of evicted (or otherwise removed) entries *
  • accumulation of cache access statistics *
* *

These features are all optional; caches can be created using all or none of them. By default, * cache instances created by {@code CacheBuilder} will not perform any type of eviction. * *

Usage example: * *

{@code
 * LoadingCache graphs = CacheBuilder.newBuilder()
 *     .maximumSize(10000)
 *     .expireAfterWrite(Duration.ofMinutes(10))
 *     .removalListener(MY_LISTENER)
 *     .build(
 *         new CacheLoader() {
 *           public Graph load(Key key) throws AnyException {
 *             return createExpensiveGraph(key);
 *           }
 *         });
 * }
* *

Or equivalently, * *

{@code
 * // In real life this would come from a command-line flag or config file
 * String spec = "maximumSize=10000,expireAfterWrite=10m";
 *
 * LoadingCache graphs = CacheBuilder.from(spec)
 *     .removalListener(MY_LISTENER)
 *     .build(
 *         new CacheLoader() {
 *           public Graph load(Key key) throws AnyException {
 *             return createExpensiveGraph(key);
 *           }
 *         });
 * }
* *

The returned cache implements all optional operations of the {@link LoadingCache} and {@link * Cache} interfaces. The {@code asMap} view (and its collection views) have weakly consistent * iterators. This means that they are safe for concurrent use, but if other threads modify the * cache after the iterator is created, it is undefined which of these changes, if any, are * reflected in that iterator. These iterators never throw {@link ConcurrentModificationException}. * *

Note: by default, the returned cache uses equality comparisons (the {@link * Object#equals equals} method) to determine equality for keys or values. However, if {@link * #weakKeys} was specified, the cache uses identity ({@code ==}) comparisons instead for keys. * Likewise, if {@link #weakValues} or {@link #softValues} was specified, the cache uses identity * comparisons for values. * *

Entries are automatically evicted from the cache when any of {@link #maximumSize(long) * maximumSize}, {@link #maximumWeight(long) maximumWeight}, {@link #expireAfterWrite * expireAfterWrite}, {@link #expireAfterAccess expireAfterAccess}, {@link #weakKeys weakKeys}, * {@link #weakValues weakValues}, or {@link #softValues softValues} are requested. * *

If {@link #maximumSize(long) maximumSize} or {@link #maximumWeight(long) maximumWeight} is * requested entries may be evicted on each cache modification. * *

If {@link #expireAfterWrite expireAfterWrite} or {@link #expireAfterAccess expireAfterAccess} * is requested entries may be evicted on each cache modification, on occasional cache accesses, or * on calls to {@link Cache#cleanUp}. Expired entries may be counted by {@link Cache#size}, but will * never be visible to read or write operations. * *

If {@link #weakKeys weakKeys}, {@link #weakValues weakValues}, or {@link #softValues * softValues} are requested, it is possible for a key or value present in the cache to be reclaimed * by the garbage collector. Entries with reclaimed keys or values may be removed from the cache on * each cache modification, on occasional cache accesses, or on calls to {@link Cache#cleanUp}; such * entries may be counted in {@link Cache#size}, but will never be visible to read or write * operations. * *

Certain cache configurations will result in the accrual of periodic maintenance tasks which * will be performed during write operations, or during occasional read operations in the absence of * writes. The {@link Cache#cleanUp} method of the returned cache will also perform maintenance, but * calling it should not be necessary with a high throughput cache. Only caches built with {@link * #removalListener removalListener}, {@link #expireAfterWrite expireAfterWrite}, {@link * #expireAfterAccess expireAfterAccess}, {@link #weakKeys weakKeys}, {@link #weakValues * weakValues}, or {@link #softValues softValues} perform periodic maintenance. * *

The caches produced by {@code CacheBuilder} are serializable, and the deserialized caches * retain all the configuration properties of the original cache. Note that the serialized form does * not include cache contents, but only configuration. * *

See the Guava User Guide article on caching for a higher-level * explanation. * * @param the most general key type this builder will be able to create caches for. This is * normally {@code Object} unless it is constrained by using a method like {@code * #removalListener}. Cache keys may not be null. * @param the most general value type this builder will be able to create caches for. This is * normally {@code Object} unless it is constrained by using a method like {@code * #removalListener}. Cache values may not be null. * @author Charles Fry * @author Kevin Bourrillion * @since 10.0 */ @GwtCompatible(emulated = true) @ElementTypesAreNonnullByDefault public final class CacheBuilder { private static final int DEFAULT_INITIAL_CAPACITY = 16; private static final int DEFAULT_CONCURRENCY_LEVEL = 4; @SuppressWarnings("GoodTime") // should be a java.time.Duration private static final int DEFAULT_EXPIRATION_NANOS = 0; @SuppressWarnings("GoodTime") // should be a java.time.Duration private static final int DEFAULT_REFRESH_NANOS = 0; static final Supplier NULL_STATS_COUNTER = Suppliers.ofInstance( new StatsCounter() { @Override public void recordHits(int count) {} @Override public void recordMisses(int count) {} @SuppressWarnings("GoodTime") // b/122668874 @Override public void recordLoadSuccess(long loadTime) {} @SuppressWarnings("GoodTime") // b/122668874 @Override public void recordLoadException(long loadTime) {} @Override public void recordEviction() {} @Override public CacheStats snapshot() { return EMPTY_STATS; } }); static final CacheStats EMPTY_STATS = new CacheStats(0, 0, 0, 0, 0, 0); /* * We avoid using a method reference or lambda here for now: * * - method reference: Inside Google, CacheBuilder is used from the implementation of a custom * ClassLoader that is sometimes used as a system classloader. That's a problem because * method-reference linking tries to look up the system classloader, and it fails because there * isn't one yet. * * - lambda: Outside Google, we got a report of a similar problem in * https://github.com/google/guava/issues/6565 */ @SuppressWarnings("AnonymousToLambda") static final Supplier CACHE_STATS_COUNTER = new Supplier() { @Override public StatsCounter get() { return new SimpleStatsCounter(); } }; enum NullListener implements RemovalListener { INSTANCE; @Override public void onRemoval(RemovalNotification notification) {} } enum OneWeigher implements Weigher { INSTANCE; @Override public int weigh(Object key, Object value) { return 1; } } static final Ticker NULL_TICKER = new Ticker() { @Override public long read() { return 0; } }; // We use a holder class to delay initialization: https://github.com/google/guava/issues/6566 private static final class LoggerHolder { static final Logger logger = Logger.getLogger(CacheBuilder.class.getName()); } static final int UNSET_INT = -1; boolean strictParsing = true; int initialCapacity = UNSET_INT; int concurrencyLevel = UNSET_INT; long maximumSize = UNSET_INT; long maximumWeight = UNSET_INT; @CheckForNull Weigher weigher; @CheckForNull Strength keyStrength; @CheckForNull Strength valueStrength; @SuppressWarnings("GoodTime") // should be a java.time.Duration long expireAfterWriteNanos = UNSET_INT; @SuppressWarnings("GoodTime") // should be a java.time.Duration long expireAfterAccessNanos = UNSET_INT; @SuppressWarnings("GoodTime") // should be a java.time.Duration long refreshNanos = UNSET_INT; @CheckForNull Equivalence keyEquivalence; @CheckForNull Equivalence valueEquivalence; @CheckForNull RemovalListener removalListener; @CheckForNull Ticker ticker; Supplier statsCounterSupplier = NULL_STATS_COUNTER; private CacheBuilder() {} /** * Constructs a new {@code CacheBuilder} instance with default settings, including strong keys, * strong values, and no automatic eviction of any kind. * *

Note that while this return type is {@code CacheBuilder}, type parameters on * the {@link #build} methods allow you to create a cache of any key and value type desired. */ public static CacheBuilder newBuilder() { return new CacheBuilder<>(); } /** * Constructs a new {@code CacheBuilder} instance with the settings specified in {@code spec}. * * @since 12.0 */ @GwtIncompatible // To be supported public static CacheBuilder from(CacheBuilderSpec spec) { return spec.toCacheBuilder().lenientParsing(); } /** * Constructs a new {@code CacheBuilder} instance with the settings specified in {@code spec}. * This is especially useful for command-line configuration of a {@code CacheBuilder}. * * @param spec a String in the format specified by {@link CacheBuilderSpec} * @since 12.0 */ @GwtIncompatible // To be supported public static CacheBuilder from(String spec) { return from(CacheBuilderSpec.parse(spec)); } /** * Enables lenient parsing. Useful for tests and spec parsing. * * @return this {@code CacheBuilder} instance (for chaining) */ @GwtIncompatible // To be supported @CanIgnoreReturnValue CacheBuilder lenientParsing() { strictParsing = false; return this; } /** * Sets a custom {@code Equivalence} strategy for comparing keys. * *

By default, the cache uses {@link Equivalence#identity} to determine key equality when * {@link #weakKeys} is specified, and {@link Equivalence#equals()} otherwise. * * @return this {@code CacheBuilder} instance (for chaining) */ @GwtIncompatible // To be supported @CanIgnoreReturnValue CacheBuilder keyEquivalence(Equivalence equivalence) { checkState(keyEquivalence == null, "key equivalence was already set to %s", keyEquivalence); keyEquivalence = checkNotNull(equivalence); return this; } Equivalence getKeyEquivalence() { return MoreObjects.firstNonNull(keyEquivalence, getKeyStrength().defaultEquivalence()); } /** * Sets a custom {@code Equivalence} strategy for comparing values. * *

By default, the cache uses {@link Equivalence#identity} to determine value equality when * {@link #weakValues} or {@link #softValues} is specified, and {@link Equivalence#equals()} * otherwise. * * @return this {@code CacheBuilder} instance (for chaining) */ @GwtIncompatible // To be supported @CanIgnoreReturnValue CacheBuilder valueEquivalence(Equivalence equivalence) { checkState( valueEquivalence == null, "value equivalence was already set to %s", valueEquivalence); this.valueEquivalence = checkNotNull(equivalence); return this; } Equivalence getValueEquivalence() { return MoreObjects.firstNonNull(valueEquivalence, getValueStrength().defaultEquivalence()); } /** * Sets the minimum total size for the internal hash tables. For example, if the initial capacity * is {@code 60}, and the concurrency level is {@code 8}, then eight segments are created, each * having a hash table of size eight. Providing a large enough estimate at construction time * avoids the need for expensive resizing operations later, but setting this value unnecessarily * high wastes memory. * * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalArgumentException if {@code initialCapacity} is negative * @throws IllegalStateException if an initial capacity was already set */ @CanIgnoreReturnValue public CacheBuilder initialCapacity(int initialCapacity) { checkState( this.initialCapacity == UNSET_INT, "initial capacity was already set to %s", this.initialCapacity); checkArgument(initialCapacity >= 0); this.initialCapacity = initialCapacity; return this; } int getInitialCapacity() { return (initialCapacity == UNSET_INT) ? DEFAULT_INITIAL_CAPACITY : initialCapacity; } /** * Guides the allowed concurrency among update operations. Used as a hint for internal sizing. The * table is internally partitioned to try to permit the indicated number of concurrent updates * without contention. Because assignment of entries to these partitions is not necessarily * uniform, the actual concurrency observed may vary. Ideally, you should choose a value to * accommodate as many threads as will ever concurrently modify the table. Using a significantly * higher value than you need can waste space and time, and a significantly lower value can lead * to thread contention. But overestimates and underestimates within an order of magnitude do not * usually have much noticeable impact. A value of one permits only one thread to modify the cache * at a time, but since read operations and cache loading computations can proceed concurrently, * this still yields higher concurrency than full synchronization. * *

Defaults to 4. Note:The default may change in the future. If you care about this * value, you should always choose it explicitly. * *

The current implementation uses the concurrency level to create a fixed number of hashtable * segments, each governed by its own write lock. The segment lock is taken once for each explicit * write, and twice for each cache loading computation (once prior to loading the new value, and * once after loading completes). Much internal cache management is performed at the segment * granularity. For example, access queues and write queues are kept per segment when they are * required by the selected eviction algorithm. As such, when writing unit tests it is not * uncommon to specify {@code concurrencyLevel(1)} in order to achieve more deterministic eviction * behavior. * *

Note that future implementations may abandon segment locking in favor of more advanced * concurrency controls. * * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalArgumentException if {@code concurrencyLevel} is nonpositive * @throws IllegalStateException if a concurrency level was already set */ @CanIgnoreReturnValue public CacheBuilder concurrencyLevel(int concurrencyLevel) { checkState( this.concurrencyLevel == UNSET_INT, "concurrency level was already set to %s", this.concurrencyLevel); checkArgument(concurrencyLevel > 0); this.concurrencyLevel = concurrencyLevel; return this; } int getConcurrencyLevel() { return (concurrencyLevel == UNSET_INT) ? DEFAULT_CONCURRENCY_LEVEL : concurrencyLevel; } /** * Specifies the maximum number of entries the cache may contain. * *

Note that the cache may evict an entry before this limit is exceeded. For example, in * the current implementation, when {@code concurrencyLevel} is greater than {@code 1}, each * resulting segment inside the cache independently limits its own size to approximately * {@code maximumSize / concurrencyLevel}. * *

When eviction is necessary, the cache evicts entries that are less likely to be used again. * For example, the cache may evict an entry because it hasn't been used recently or very often. * *

If {@code maximumSize} is zero, elements will be evicted immediately after being loaded into * cache. This can be useful in testing, or to disable caching temporarily. * *

This feature cannot be used in conjunction with {@link #maximumWeight}. * * @param maximumSize the maximum size of the cache * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalArgumentException if {@code maximumSize} is negative * @throws IllegalStateException if a maximum size or weight was already set */ @CanIgnoreReturnValue public CacheBuilder maximumSize(long maximumSize) { checkState( this.maximumSize == UNSET_INT, "maximum size was already set to %s", this.maximumSize); checkState( this.maximumWeight == UNSET_INT, "maximum weight was already set to %s", this.maximumWeight); checkState(this.weigher == null, "maximum size can not be combined with weigher"); checkArgument(maximumSize >= 0, "maximum size must not be negative"); this.maximumSize = maximumSize; return this; } /** * Specifies the maximum weight of entries the cache may contain. Weight is determined using the * {@link Weigher} specified with {@link #weigher}, and use of this method requires a * corresponding call to {@link #weigher} prior to calling {@link #build}. * *

Note that the cache may evict an entry before this limit is exceeded. For example, in * the current implementation, when {@code concurrencyLevel} is greater than {@code 1}, each * resulting segment inside the cache independently limits its own weight to approximately * {@code maximumWeight / concurrencyLevel}. * *

When eviction is necessary, the cache evicts entries that are less likely to be used again. * For example, the cache may evict an entry because it hasn't been used recently or very often. * *

If {@code maximumWeight} is zero, elements will be evicted immediately after being loaded * into cache. This can be useful in testing, or to disable caching temporarily. * *

Note that weight is only used to determine whether the cache is over capacity; it has no * effect on selecting which entry should be evicted next. * *

This feature cannot be used in conjunction with {@link #maximumSize}. * * @param maximumWeight the maximum total weight of entries the cache may contain * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalArgumentException if {@code maximumWeight} is negative * @throws IllegalStateException if a maximum weight or size was already set * @since 11.0 */ @GwtIncompatible // To be supported @CanIgnoreReturnValue public CacheBuilder maximumWeight(long maximumWeight) { checkState( this.maximumWeight == UNSET_INT, "maximum weight was already set to %s", this.maximumWeight); checkState( this.maximumSize == UNSET_INT, "maximum size was already set to %s", this.maximumSize); checkArgument(maximumWeight >= 0, "maximum weight must not be negative"); this.maximumWeight = maximumWeight; return this; } /** * Specifies the weigher to use in determining the weight of entries. Entry weight is taken into * consideration by {@link #maximumWeight(long)} when determining which entries to evict, and use * of this method requires a corresponding call to {@link #maximumWeight(long)} prior to calling * {@link #build}. Weights are measured and recorded when entries are inserted into the cache, and * are thus effectively static during the lifetime of a cache entry. * *

When the weight of an entry is zero it will not be considered for size-based eviction * (though it still may be evicted by other means). * *

Important note: Instead of returning this as a {@code CacheBuilder} * instance, this method returns {@code CacheBuilder}. From this point on, either the * original reference or the returned reference may be used to complete configuration and build * the cache, but only the "generic" one is type-safe. That is, it will properly prevent you from * building caches whose key or value types are incompatible with the types accepted by the * weigher already provided; the {@code CacheBuilder} type cannot do this. For best results, * simply use the standard method-chaining idiom, as illustrated in the documentation at top, * configuring a {@code CacheBuilder} and building your {@link Cache} all in a single statement. * *

Warning: if you ignore the above advice, and use this {@code CacheBuilder} to build a * cache whose key or value type is incompatible with the weigher, you will likely experience a * {@link ClassCastException} at some undefined point in the future. * * @param weigher the weigher to use in calculating the weight of cache entries * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalStateException if a weigher was already set or {@link #maximumSize} was * previously called * @since 11.0 */ @GwtIncompatible // To be supported @CanIgnoreReturnValue // TODO(b/27479612): consider removing this public CacheBuilder weigher( Weigher weigher) { checkState(this.weigher == null); if (strictParsing) { checkState( this.maximumSize == UNSET_INT, "weigher can not be combined with maximum size (%s provided)", this.maximumSize); } // safely limiting the kinds of caches this can produce @SuppressWarnings("unchecked") CacheBuilder me = (CacheBuilder) this; me.weigher = checkNotNull(weigher); return me; } long getMaximumWeight() { if (expireAfterWriteNanos == 0 || expireAfterAccessNanos == 0) { return 0; } return (weigher == null) ? maximumSize : maximumWeight; } // Make a safe contravariant cast now so we don't have to do it over and over. @SuppressWarnings("unchecked") Weigher getWeigher() { return (Weigher) MoreObjects.firstNonNull(weigher, OneWeigher.INSTANCE); } /** * Specifies that each key (not value) stored in the cache should be wrapped in a {@link * WeakReference} (by default, strong references are used). * *

Warning: when this method is used, the resulting cache will use identity ({@code ==}) * comparison to determine equality of keys. Its {@link Cache#asMap} view will therefore * technically violate the {@link Map} specification (in the same way that {@link IdentityHashMap} * does). * *

Entries with keys that have been garbage collected may be counted in {@link Cache#size}, but * will never be visible to read or write operations; such entries are cleaned up as part of the * routine maintenance described in the class javadoc. * * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalStateException if the key strength was already set */ @GwtIncompatible // java.lang.ref.WeakReference @CanIgnoreReturnValue public CacheBuilder weakKeys() { return setKeyStrength(Strength.WEAK); } @CanIgnoreReturnValue CacheBuilder setKeyStrength(Strength strength) { checkState(keyStrength == null, "Key strength was already set to %s", keyStrength); keyStrength = checkNotNull(strength); return this; } Strength getKeyStrength() { return MoreObjects.firstNonNull(keyStrength, Strength.STRONG); } /** * Specifies that each value (not key) stored in the cache should be wrapped in a {@link * WeakReference} (by default, strong references are used). * *

Weak values will be garbage collected once they are weakly reachable. This makes them a poor * candidate for caching; consider {@link #softValues} instead. * *

Note: when this method is used, the resulting cache will use identity ({@code ==}) * comparison to determine equality of values. * *

Entries with values that have been garbage collected may be counted in {@link Cache#size}, * but will never be visible to read or write operations; such entries are cleaned up as part of * the routine maintenance described in the class javadoc. * * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalStateException if the value strength was already set */ @GwtIncompatible // java.lang.ref.WeakReference @CanIgnoreReturnValue public CacheBuilder weakValues() { return setValueStrength(Strength.WEAK); } /** * Specifies that each value (not key) stored in the cache should be wrapped in a {@link * SoftReference} (by default, strong references are used). Softly-referenced objects will be * garbage-collected in a globally least-recently-used manner, in response to memory * demand. * *

Warning: in most circumstances it is better to set a per-cache {@linkplain * #maximumSize(long) maximum size} instead of using soft references. You should only use this * method if you are well familiar with the practical consequences of soft references. * *

Note: when this method is used, the resulting cache will use identity ({@code ==}) * comparison to determine equality of values. * *

Entries with values that have been garbage collected may be counted in {@link Cache#size}, * but will never be visible to read or write operations; such entries are cleaned up as part of * the routine maintenance described in the class javadoc. * * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalStateException if the value strength was already set */ @GwtIncompatible // java.lang.ref.SoftReference @CanIgnoreReturnValue public CacheBuilder softValues() { return setValueStrength(Strength.SOFT); } @CanIgnoreReturnValue CacheBuilder setValueStrength(Strength strength) { checkState(valueStrength == null, "Value strength was already set to %s", valueStrength); valueStrength = checkNotNull(strength); return this; } Strength getValueStrength() { return MoreObjects.firstNonNull(valueStrength, Strength.STRONG); } /** * Specifies that each entry should be automatically removed from the cache once a fixed duration * has elapsed after the entry's creation, or the most recent replacement of its value. * *

When {@code duration} is zero, this method hands off to {@link #maximumSize(long) * maximumSize}{@code (0)}, ignoring any otherwise-specified maximum size or weight. This can be * useful in testing, or to disable caching temporarily without a code change. * *

Expired entries may be counted in {@link Cache#size}, but will never be visible to read or * write operations. Expired entries are cleaned up as part of the routine maintenance described * in the class javadoc. * * @param duration the length of time after an entry is created that it should be automatically * removed * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #expireAfterWrite} was already set * @throws ArithmeticException for durations greater than +/- approximately 292 years * @since 25.0 */ @J2ObjCIncompatible @GwtIncompatible // java.time.Duration @SuppressWarnings("GoodTime") // java.time.Duration decomposition @CanIgnoreReturnValue public CacheBuilder expireAfterWrite(java.time.Duration duration) { return expireAfterWrite(toNanosSaturated(duration), TimeUnit.NANOSECONDS); } /** * Specifies that each entry should be automatically removed from the cache once a fixed duration * has elapsed after the entry's creation, or the most recent replacement of its value. * *

When {@code duration} is zero, this method hands off to {@link #maximumSize(long) * maximumSize}{@code (0)}, ignoring any otherwise-specified maximum size or weight. This can be * useful in testing, or to disable caching temporarily without a code change. * *

Expired entries may be counted in {@link Cache#size}, but will never be visible to read or * write operations. Expired entries are cleaned up as part of the routine maintenance described * in the class javadoc. * *

If you can represent the duration as a {@link java.time.Duration} (which should be preferred * when feasible), use {@link #expireAfterWrite(Duration)} instead. * * @param duration the length of time after an entry is created that it should be automatically * removed * @param unit the unit that {@code duration} is expressed in * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #expireAfterWrite} was already set */ @SuppressWarnings("GoodTime") // should accept a java.time.Duration @CanIgnoreReturnValue public CacheBuilder expireAfterWrite(long duration, TimeUnit unit) { checkState( expireAfterWriteNanos == UNSET_INT, "expireAfterWrite was already set to %s ns", expireAfterWriteNanos); checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit); this.expireAfterWriteNanos = unit.toNanos(duration); return this; } @SuppressWarnings("GoodTime") // nanos internally, should be Duration long getExpireAfterWriteNanos() { return (expireAfterWriteNanos == UNSET_INT) ? DEFAULT_EXPIRATION_NANOS : expireAfterWriteNanos; } /** * Specifies that each entry should be automatically removed from the cache once a fixed duration * has elapsed after the entry's creation, the most recent replacement of its value, or its last * access. Access time is reset by all cache read and write operations (including {@code * Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by {@code * containsKey(Object)}, nor by operations on the collection-views of {@link Cache#asMap}}. So, * for example, iterating through {@code Cache.asMap().entrySet()} does not reset access time for * the entries you retrieve. * *

When {@code duration} is zero, this method hands off to {@link #maximumSize(long) * maximumSize}{@code (0)}, ignoring any otherwise-specified maximum size or weight. This can be * useful in testing, or to disable caching temporarily without a code change. * *

Expired entries may be counted in {@link Cache#size}, but will never be visible to read or * write operations. Expired entries are cleaned up as part of the routine maintenance described * in the class javadoc. * * @param duration the length of time after an entry is last accessed that it should be * automatically removed * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #expireAfterAccess} was already set * @throws ArithmeticException for durations greater than +/- approximately 292 years * @since 25.0 */ @J2ObjCIncompatible @GwtIncompatible // java.time.Duration @SuppressWarnings("GoodTime") // java.time.Duration decomposition @CanIgnoreReturnValue public CacheBuilder expireAfterAccess(java.time.Duration duration) { return expireAfterAccess(toNanosSaturated(duration), TimeUnit.NANOSECONDS); } /** * Specifies that each entry should be automatically removed from the cache once a fixed duration * has elapsed after the entry's creation, the most recent replacement of its value, or its last * access. Access time is reset by all cache read and write operations (including {@code * Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by {@code * containsKey(Object)}, nor by operations on the collection-views of {@link Cache#asMap}. So, for * example, iterating through {@code Cache.asMap().entrySet()} does not reset access time for the * entries you retrieve. * *

When {@code duration} is zero, this method hands off to {@link #maximumSize(long) * maximumSize}{@code (0)}, ignoring any otherwise-specified maximum size or weight. This can be * useful in testing, or to disable caching temporarily without a code change. * *

Expired entries may be counted in {@link Cache#size}, but will never be visible to read or * write operations. Expired entries are cleaned up as part of the routine maintenance described * in the class javadoc. * *

If you can represent the duration as a {@link java.time.Duration} (which should be preferred * when feasible), use {@link #expireAfterAccess(Duration)} instead. * * @param duration the length of time after an entry is last accessed that it should be * automatically removed * @param unit the unit that {@code duration} is expressed in * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #expireAfterAccess} was already set */ @SuppressWarnings("GoodTime") // should accept a java.time.Duration @CanIgnoreReturnValue public CacheBuilder expireAfterAccess(long duration, TimeUnit unit) { checkState( expireAfterAccessNanos == UNSET_INT, "expireAfterAccess was already set to %s ns", expireAfterAccessNanos); checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit); this.expireAfterAccessNanos = unit.toNanos(duration); return this; } @SuppressWarnings("GoodTime") // nanos internally, should be Duration long getExpireAfterAccessNanos() { return (expireAfterAccessNanos == UNSET_INT) ? DEFAULT_EXPIRATION_NANOS : expireAfterAccessNanos; } /** * Specifies that active entries are eligible for automatic refresh once a fixed duration has * elapsed after the entry's creation, or the most recent replacement of its value. The semantics * of refreshes are specified in {@link LoadingCache#refresh}, and are performed by calling {@link * CacheLoader#reload}. * *

As the default implementation of {@link CacheLoader#reload} is synchronous, it is * recommended that users of this method override {@link CacheLoader#reload} with an asynchronous * implementation; otherwise refreshes will be performed during unrelated cache read and write * operations. * *

Currently automatic refreshes are performed when the first stale request for an entry * occurs. The request triggering refresh will make a synchronous call to {@link * CacheLoader#reload} * to obtain a future of the new value. If the returned future is already complete, it is returned * immediately. Otherwise, the old value is returned. * *

Note: all exceptions thrown during refresh will be logged and then swallowed. * * @param duration the length of time after an entry is created that it should be considered * stale, and thus eligible for refresh * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #refreshAfterWrite} was already set * @throws ArithmeticException for durations greater than +/- approximately 292 years * @since 25.0 */ @J2ObjCIncompatible @GwtIncompatible // java.time.Duration @SuppressWarnings("GoodTime") // java.time.Duration decomposition @CanIgnoreReturnValue public CacheBuilder refreshAfterWrite(java.time.Duration duration) { return refreshAfterWrite(toNanosSaturated(duration), TimeUnit.NANOSECONDS); } /** * Specifies that active entries are eligible for automatic refresh once a fixed duration has * elapsed after the entry's creation, or the most recent replacement of its value. The semantics * of refreshes are specified in {@link LoadingCache#refresh}, and are performed by calling {@link * CacheLoader#reload}. * *

As the default implementation of {@link CacheLoader#reload} is synchronous, it is * recommended that users of this method override {@link CacheLoader#reload} with an asynchronous * implementation; otherwise refreshes will be performed during unrelated cache read and write * operations. * *

Currently automatic refreshes are performed when the first stale request for an entry * occurs. The request triggering refresh will make a synchronous call to {@link * CacheLoader#reload} * and immediately return the new value if the returned future is complete, and the old value * otherwise. * *

Note: all exceptions thrown during refresh will be logged and then swallowed. * *

If you can represent the duration as a {@link java.time.Duration} (which should be preferred * when feasible), use {@link #refreshAfterWrite(Duration)} instead. * * @param duration the length of time after an entry is created that it should be considered * stale, and thus eligible for refresh * @param unit the unit that {@code duration} is expressed in * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #refreshAfterWrite} was already set * @since 11.0 */ @GwtIncompatible // To be supported (synchronously). @SuppressWarnings("GoodTime") // should accept a java.time.Duration @CanIgnoreReturnValue public CacheBuilder refreshAfterWrite(long duration, TimeUnit unit) { checkNotNull(unit); checkState(refreshNanos == UNSET_INT, "refresh was already set to %s ns", refreshNanos); checkArgument(duration > 0, "duration must be positive: %s %s", duration, unit); this.refreshNanos = unit.toNanos(duration); return this; } @SuppressWarnings("GoodTime") // nanos internally, should be Duration long getRefreshNanos() { return (refreshNanos == UNSET_INT) ? DEFAULT_REFRESH_NANOS : refreshNanos; } /** * Specifies a nanosecond-precision time source for this cache. By default, {@link * System#nanoTime} is used. * *

The primary intent of this method is to facilitate testing of caches with a fake or mock * time source. * * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalStateException if a ticker was already set */ @CanIgnoreReturnValue public CacheBuilder ticker(Ticker ticker) { checkState(this.ticker == null); this.ticker = checkNotNull(ticker); return this; } Ticker getTicker(boolean recordsTime) { if (ticker != null) { return ticker; } return recordsTime ? Ticker.systemTicker() : NULL_TICKER; } /** * Specifies a listener instance that caches should notify each time an entry is removed for any * {@linkplain RemovalCause reason}. Each cache created by this builder will invoke this listener * as part of the routine maintenance described in the class documentation above. * *

Warning: after invoking this method, do not continue to use this cache builder * reference; instead use the reference this method returns. At runtime, these point to the * same instance, but only the returned reference has the correct generic type information to * ensure type safety. For best results, use the standard method-chaining idiom illustrated in the * class documentation above, configuring a builder and building your cache in a single statement. * Failure to heed this advice can result in a {@link ClassCastException} being thrown by a cache * operation at some undefined point in the future. * *

Warning: any exception thrown by {@code listener} will not be propagated to * the {@code Cache} user, only logged via a {@link Logger}. * * @return the cache builder reference that should be used instead of {@code this} for any * remaining configuration and cache building * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalStateException if a removal listener was already set */ public CacheBuilder removalListener( RemovalListener listener) { checkState(this.removalListener == null); // safely limiting the kinds of caches this can produce @SuppressWarnings("unchecked") CacheBuilder me = (CacheBuilder) this; me.removalListener = checkNotNull(listener); return me; } // Make a safe contravariant cast now so we don't have to do it over and over. @SuppressWarnings("unchecked") RemovalListener getRemovalListener() { return (RemovalListener) MoreObjects.firstNonNull(removalListener, NullListener.INSTANCE); } /** * Enable the accumulation of {@link CacheStats} during the operation of the cache. Without this * {@link Cache#stats} will return zero for all statistics. Note that recording stats requires * bookkeeping to be performed with each operation, and thus imposes a performance penalty on * cache operation. * * @return this {@code CacheBuilder} instance (for chaining) * @since 12.0 (previously, stats collection was automatic) */ @CanIgnoreReturnValue public CacheBuilder recordStats() { statsCounterSupplier = CACHE_STATS_COUNTER; return this; } boolean isRecordingStats() { return statsCounterSupplier == CACHE_STATS_COUNTER; } Supplier getStatsCounterSupplier() { return statsCounterSupplier; } /** * Builds a cache, which either returns an already-loaded value for a given key or atomically * computes or retrieves it using the supplied {@code CacheLoader}. If another thread is currently * loading the value for this key, simply waits for that thread to finish and returns its loaded * value. Note that multiple threads can concurrently load values for distinct keys. * *

This method does not alter the state of this {@code CacheBuilder} instance, so it can be * invoked again to create multiple independent caches. * * @param loader the cache loader used to obtain new values * @return a cache having the requested features */ public LoadingCache build( CacheLoader loader) { checkWeightWithWeigher(); return new LocalCache.LocalLoadingCache<>(this, loader); } /** * Builds a cache which does not automatically load values when keys are requested. * *

Consider {@link #build(CacheLoader)} instead, if it is feasible to implement a {@code * CacheLoader}. * *

This method does not alter the state of this {@code CacheBuilder} instance, so it can be * invoked again to create multiple independent caches. * * @return a cache having the requested features * @since 11.0 */ public Cache build() { checkWeightWithWeigher(); checkNonLoadingCache(); return new LocalCache.LocalManualCache<>(this); } private void checkNonLoadingCache() { checkState(refreshNanos == UNSET_INT, "refreshAfterWrite requires a LoadingCache"); } private void checkWeightWithWeigher() { if (weigher == null) { checkState(maximumWeight == UNSET_INT, "maximumWeight requires weigher"); } else { if (strictParsing) { checkState(maximumWeight != UNSET_INT, "weigher requires maximumWeight"); } else { if (maximumWeight == UNSET_INT) { LoggerHolder.logger.log( Level.WARNING, "ignoring weigher specified without maximumWeight"); } } } } /** * Returns a string representation for this CacheBuilder instance. The exact form of the returned * string is not specified. */ @Override public String toString() { MoreObjects.ToStringHelper s = MoreObjects.toStringHelper(this); if (initialCapacity != UNSET_INT) { s.add("initialCapacity", initialCapacity); } if (concurrencyLevel != UNSET_INT) { s.add("concurrencyLevel", concurrencyLevel); } if (maximumSize != UNSET_INT) { s.add("maximumSize", maximumSize); } if (maximumWeight != UNSET_INT) { s.add("maximumWeight", maximumWeight); } if (expireAfterWriteNanos != UNSET_INT) { s.add("expireAfterWrite", expireAfterWriteNanos + "ns"); } if (expireAfterAccessNanos != UNSET_INT) { s.add("expireAfterAccess", expireAfterAccessNanos + "ns"); } if (keyStrength != null) { s.add("keyStrength", Ascii.toLowerCase(keyStrength.toString())); } if (valueStrength != null) { s.add("valueStrength", Ascii.toLowerCase(valueStrength.toString())); } if (keyEquivalence != null) { s.addValue("keyEquivalence"); } if (valueEquivalence != null) { s.addValue("valueEquivalence"); } if (removalListener != null) { s.addValue("removalListener"); } return s.toString(); } /** * Returns the number of nanoseconds of the given duration without throwing or overflowing. * *

Instead of throwing {@link ArithmeticException}, this method silently saturates to either * {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE}. This behavior can be useful when decomposing * a duration in order to call a legacy API which requires a {@code long, TimeUnit} pair. */ @GwtIncompatible // java.time.Duration @SuppressWarnings("GoodTime") // duration decomposition private static long toNanosSaturated(java.time.Duration duration) { // Using a try/catch seems lazy, but the catch block will rarely get invoked (except for // durations longer than approximately +/- 292 years). try { return duration.toNanos(); } catch (ArithmeticException tooBig) { return duration.isNegative() ? Long.MIN_VALUE : Long.MAX_VALUE; } } }