Introduction: Why is caching needed in Java?
Modern Java applications often face performance issues when working with databases, external APIs, or heavy computations. Each repeated request to a slow resource increases response time and infrastructure load. The solution to this problem is caching.
Caching in Java allows you to temporarily store frequently requested data in fast access (usually in RAM), so that on subsequent requests it can be returned instantly, bypassing expensive operations. This is critically important for high-load systems, web applications, and microservices.
In this article, we will analyze three main approaches to caching in Java: distributed solutions (Redis, Memcached) and embedded (in-memory) caches. You will learn when to choose which tool and see code examples for each of them.
1. Embedded caching in Java: Ehcache, Caffeine, and HashMap
Embedded caches are libraries that run inside your application's JVM. They do not require setting up external servers and are ideal for monolithic applications or microservices with a small amount of data.
1.1. Simple HashMap cache (not for production)
The simplest way is to use a ConcurrentHashMap. However, this approach lacks mechanisms for eviction, TTL (time-to-live), and memory control.
import java.util.concurrent.ConcurrentHashMap;
public class SimpleCache<K, V> { private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();
public V get(K key) { return cache.get(key); }
public void put(K key, V value) { cache.put(key, value); }
public void evict(K key) { cache.remove(key); }}Disadvantages: no automatic eviction, can lead to OutOfMemoryError.
1.2. Caffeine — the modern standard in Java
Caffeine is a high-performance caching library for Java 8+. It supports automatic eviction based on time, size, and frequency of use.
import com.github.benmanes.caffeine.cache.Cache;import com.github.benmanes.caffeine.cache.Caffeine;import java.util.concurrent.TimeUnit;
public class CaffeineExample { public static void main(String[] args) { Cache<String, String> cache = Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .maximumSize(1000) .build();
// Write cache.put("user:123", "John Doe");
// Read String user = cache.getIfPresent("user:123"); System.out.println("Cached user: " + user); }}Advantages of Caffeine: high speed, configurable eviction policy (LRU, LFU), asynchronous loading.
1.3. Ehcache — time-tested
Ehcache is one of the oldest caching libraries for Java. It supports disk storage and clustering.
import org.ehcache.Cache;import org.ehcache.CacheManager;import org.ehcache.config.builders.CacheConfigurationBuilder;import org.ehcache.config.builders.CacheManagerBuilder;import org.ehcache.config.builders.ResourcePoolsBuilder;
public class EhcacheExample { public static void main(String[] args) { CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("myCache", CacheConfigurationBuilder.newCacheConfigurationBuilder( String.class, String.class, ResourcePoolsBuilder.heap(100))) .build(); cacheManager.init();
Cache<String, String> cache = cacheManager.getCache("m