Skip to main content
This guide will get you from zero to a working Caffeine cache in just a few minutes.

Prerequisites

Make sure you have:
  • Java 11 or higher installed
  • Caffeine added to your project (see Installation)

Basic cache usage

1

Import Caffeine classes

Start by importing the core Caffeine classes:
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
2

Build a cache

Create a cache with a maximum size:
Cache<String, String> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .build();
This creates a cache that holds up to 10,000 entries. When the limit is exceeded, Caffeine evicts entries based on frequency and recency.
3

Add entries

Store values in the cache:
cache.put("user:123", "John Doe");
cache.put("user:456", "Jane Smith");
4

Retrieve entries

Get values from the cache:
String user = cache.getIfPresent("user:123");
System.out.println(user); // Prints: John Doe

String notFound = cache.getIfPresent("user:999");
System.out.println(notFound); // Prints: null
5

Compute on miss

Load values automatically if not present:
String user = cache.get("user:789", key -> {
    // This function is called only if key is not in cache
    return fetchUserFromDatabase(key);
});
The mapping function is called at most once per key, and the computation is atomic.

Loading cache

Use a LoadingCache when you want the cache to automatically load missing entries using a consistent loading strategy.
1

Import LoadingCache

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
2

Build with a loader

Create a cache that automatically loads missing entries:
LoadingCache<String, User> userCache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .build(key -> loadUserFromDatabase(key));
The loader function is called automatically whenever a key is not found in the cache.
3

Access entries

Simply call get() - the cache handles loading automatically:
// Loads from database if not in cache
User user = userCache.get("user:123");

// Already cached, returns immediately
User sameUser = userCache.get("user:123");
4

Bulk loading

Load multiple entries efficiently:
Map<String, User> users = userCache.getAll(
    List.of("user:123", "user:456", "user:789")
);
Implement CacheLoader.loadAll() for even more efficient bulk loading from your data source.

Time-based expiration

Expire entries after a fixed duration:
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import java.time.Duration;

LoadingCache<String, String> cache = Caffeine.newBuilder()
    .expireAfterWrite(Duration.ofMinutes(5))
    .build(key -> loadData(key));
Expire after write: Entries expire after a fixed duration from creation or last update. Expire after access: Entries expire after a fixed duration since last read or write.

Refresh entries

Keep your cache warm by automatically refreshing stale entries:
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import java.time.Duration;

LoadingCache<String, String> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(Duration.ofMinutes(5))
    .refreshAfterWrite(Duration.ofMinutes(1))
    .build(key -> loadExpensiveData(key));
When an entry is accessed after the refresh time, Caffeine asynchronously reloads it while returning the old value. This ensures requests remain fast while keeping data fresh.

Complete example

Here’s a complete working example that demonstrates a real-world use case:
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import java.time.Duration;

public class UserCacheExample {
    
    static class User {
        String id;
        String name;
        String email;
        
        User(String id, String name, String email) {
            this.id = id;
            this.name = name;
            this.email = email;
        }
        
        @Override
        public String toString() {
            return "User{id='" + id + "', name='" + name + "', email='" + email + "'}";
        }
    }
    
    private static User loadUserFromDatabase(String userId) {
        // Simulate database query
        System.out.println("Loading user " + userId + " from database...");
        try {
            Thread.sleep(100); // Simulate network latency
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return new User(userId, "User " + userId, userId + "@example.com");
    }
    
    public static void main(String[] args) {
        // Create a loading cache with size and time-based eviction
        LoadingCache<String, User> userCache = Caffeine.newBuilder()
            .maximumSize(10_000)
            .expireAfterWrite(Duration.ofMinutes(10))
            .recordStats() // Enable statistics
            .build(key -> loadUserFromDatabase(key));
        
        // First access - loads from "database"
        System.out.println("First access:");
        User user1 = userCache.get("123");
        System.out.println(user1);
        
        // Second access - returns cached value
        System.out.println("\nSecond access:");
        User user2 = userCache.get("123");
        System.out.println(user2);
        
        // Load multiple users
        System.out.println("\nBulk load:");
        var users = userCache.getAll(java.util.List.of("456", "789", "123"));
        users.forEach((id, user) -> System.out.println(user));
        
        // Print cache statistics
        System.out.println("\nCache statistics:");
        System.out.println(userCache.stats());
    }
}
Output:
First access:
Loading user 123 from database...
User{id='123', name='User 123', email='[email protected]'}

Second access:
User{id='123', name='User 123', email='[email protected]'}

Bulk load:
Loading user 456 from database...
Loading user 789 from database...
User{id='456', name='User 456', email='[email protected]'}
User{id='789', name='User 789', email='[email protected]'}
User{id='123', name='User 123', email='[email protected]'}

Cache statistics:
CacheStats{hitCount=1, missCount=3, loadSuccessCount=3, loadFailureCount=0, totalLoadTime=300000000, evictionCount=0, evictionWeight=0}

Configuration options summary

Here’s a quick reference of common cache configurations:
Cache<K, V> cache = Caffeine.newBuilder()
    // Size-based eviction
    .maximumSize(10_000)                              // Maximum 10k entries
    
    // Time-based expiration
    .expireAfterWrite(Duration.ofMinutes(5))         // Expire 5 min after write
    .expireAfterAccess(Duration.ofMinutes(10))       // Expire 10 min after access
    
    // Automatic refresh
    .refreshAfterWrite(Duration.ofMinutes(1))        // Refresh after 1 minute
    
    // Performance monitoring
    .recordStats()                                    // Enable statistics
    
    // Reference-based eviction
    .weakKeys()                                       // Use weak references for keys
    .softValues()                                     // Use soft references for values
    
    .build();

Next steps

Now that you have a working cache, explore more advanced features:

Core concepts

Learn about eviction policies, expiration strategies, and cache types

API reference

Explore all configuration options and API methods

Statistics

Monitor cache performance with built-in statistics

Performance

Learn tips and patterns for optimal performance

Build docs developers (and LLMs) love