Skip to content
Ben Manes edited this page Mar 22, 2015 · 8 revisions

A Cache is similar to ConcurrentMap, but not quite the same. The most fundamental difference is that a ConcurrentMap persists all elements that are added to it until they are explicitly removed. A Cache on the other hand is generally configured to evict entries automatically, in order to constrain its memory footprint. In some cases a LoadingCache or AsyncLoadingCache can be useful even if it doesn't evict entries, due to its automatic cache loading.

Caffeine provide flexible construction to create a cache with any combination of the following features:

  • automatic loading of entries into the cache, optionally asynchronously
  • least-recently-used eviction when a maximum size is exceeded
  • time-based expiration of entries, measured since last access or last write
  • keys automatically wrapped in weak references
  • values automatically wrapped in weak or soft references
  • notification of evicted (or otherwise removed) entries
  • accumulation of cache access statistics

Population

Manual

Cache<Key, Graph> graphs = Caffeine.newBuilder()
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .maximumSize(10_000)
    .removalListener(MY_LISTENER)
    .build();
// Lookup an entry
cache.getIfPresent(key);
// Lookup and compute an entry if absent
cache.get(key, k -> createExpensiveGraph(key));
// Insert or update an entry
cache.put(key, value);
// Remove an entry
cache.invalidate(key);

The Cache interface allows for explicit control of looking up, updating, and invalidating entries.

Entries may be inserted into the cache directly with cache.put(key, value). This overwrites any previous entry in the cache for the specified key. Changes can also be made to a cache using any of the ConcurrentMap methods exposed by the Cache.asMap() view.

Loading

LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .removalListener(MY_LISTENER)
    .build(key -> createExpensiveGraph(key));
// Lookup and compute an entry if absent
cache.get(key);
// Lookup and compute entries that are absent
cache.getAll(keys);

A LoadingCache is a Cache built with an attached CacheLoader.

Bulk lookups can be performed with the method getAll. By default, getAll will issue a separate call to CacheLoader.load for each key which is absent from the cache. When bulk retrieval is more efficient than many individual lookups, you can override CacheLoader.loadAll to exploit this.

Note that you can write a CacheLoader.loadAll implementation that loads values for keys that were not specifically requested. For example, if computing the value of any key from some group gives you the value for all keys in the group, loadAll might load the rest of the group at the same time.

Asynchronous Loading

AsyncLoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .removalListener(MY_LISTENER)
    .buildAsync(key -> createExpensiveGraph(key));
// Lookup and compute an entry if absent
cache.get(key);
// Lookup and compute entries that are absent
cache.getAll(keys);

A AsyncLoadingCache is a LoadingCache variant that computes entries on an Executor and returns a CompletableFuture. This allows utilizing caches with the popular reactive programming model.

The synchronous() view provides a LoadingCache where that blocks until the asynchronous computation completes.

Clone this wiki locally