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
Import Caffeine classes
Start by importing the core Caffeine classes: import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
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.
Add entries
Store values in the cache: cache . put ( "user:123" , "John Doe" );
cache . put ( "user:456" , "Jane Smith" );
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
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.
Import LoadingCache
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
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.
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" );
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:
Expire after write
Expire after access
Combined size and time
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 ( " \n Second access:" );
User user2 = userCache . get ( "123" );
System . out . println (user2);
// Load multiple users
System . out . println ( " \n Bulk 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 ( " \n Cache 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