Skip to content
Chris Povirk edited this page Jan 5, 2023 · 19 revisions
// Guava's LoadingCache interface
LoadingCache<Key, Graph> graphs = CaffeinatedGuava.build(
    Caffeine.newBuilder().maximumSize(10_000),
    new CacheLoader<Key, Graph>() { // Guava's CacheLoader
        @Override public Graph load(Key key) throws Exception {
          return createExpensiveGraph(key);
        }
    });

API compatibility

Caffeine provides an adapter to expose its caches using Guava's interfaces. These adapters provide the same API contract with the implementation caveats below. Where possible Guava like behavior is emulated and validated with a port of Guava's test suite.

When transitioning to Caffeine's interfaces, please note that while the two caches have similar method names the behavior may be slightly different. Please consult the JavaDoc to compare usages more thoroughly.

Maximum size (or weighted size)

Guava will evict prior to reaching the maximum size using the LRU algorithm. Caffeine will evict once the threshold has been crossed using the Window TinyLFU algorithm.

Immediate expiration

Guava converts immediate expiration (expireAfterAccess(0, timeUnit) and expireAfterWrite(0, timeUnit)) into the setting maximum size equals zero. This results in the removal notification supplying the size as the cause for removal instead of expiration. Caffeine correctly identifies expiration as the removal cause.

Replacement notification

Guava notifies the removal listener when the entry's value is replaced for any reason. Caffeine does not notify on reference equality of the previous and replacement value.

Invalidation with concurrent computations

Guava will ignore entries during invalidation while they are still being computed. In Caffeine each entry being invalidated will block the caller until it has finished computing and will then be removed. However, computing entries may be skipped by an invalidateAll() due to those keys being suppressed by the underlying hash table. If an asynchronous cache is used instead then invalidation is non-blocking as the incomplete future will be removed and the removal notification delegated to the computing thread.

Asynchronous maintenance

Guava amortizes maintenance work on the calling thread during read and write operations. Caffeine delegates this periodic maintenance to the configured executor (default: ForkJoinPool.commonPool()). An explicit cleanUp() is invoked on the calling thread.

Asynchronous notifications

Guava processes removal notifications from a queue that any calling thread may take from. Caffeine delegates to the configured executor (default: ForkJoinPool.commonPool()).

Asynchronous refresh

Guava recomputes an entry on the thread requesting a refresh. Caffeine delegates to the configured executor (default: ForkJoinPool.commonPool()).

Computing null values

Guava throws an exception when a null value has been computed and retains the entry if due to a refresh. Caffeine returns the null value and, if the computation was due to a refresh, removes the entry. If the Guava adapters are used, Caffeine will behave as Guava does if built with a Guava CacheLoader.

Computing as a Map

Guava versions prior to 21.0 inherit the non-atomic ConcurrentMap default methods (compute, computeIfAbsent, computeIfPresent, and merge). Caffeine implements atomic versions of these Java 8 additions.

CacheStats

Guava's CacheStats.loadExceptionCount() and CacheStats.loadExceptionRate() are renamed to CacheStats.loadFailureCount() and CacheStats.loadFailureRate() respectively in Caffeine. This change is due to null computed values being treated as load failures and not as exceptions.

Android & GWT compatibility

Caffeine does not provide compatibility due to those platforms not supporting Java 8.

Concurrency level

Caffeine's data structures adapt automatically to different levels of concurrency. In case of unusual scenarios that see significant write contention, consider the mitigations documented in the FAQ.