In Java, an in-memory cache can be implemented using ConcurrentMap
very efficiently, but it has only the problem that objects can not be evicted automatically after a certain time or limit, we need to remove entries manually.
In many situations, the in-memory cache can be very useful to increase speed, but obviously, it will also increase memory footprint. These also do not persist data in files. If you need to persyst data or make it available across process and network then you can try Memcached.
Here I am writting about some good libraries and approaches to use in-memory cache in your application. Guava lib is my favorite one.
Google’s Guava Lib
Guava is a set of open-source core Java libraries from Google that includes collection types multimap and multiset, immutable collections, a graph library, and utilities for concurrency, I/O, hashing, caching, primitives, strings, and more. CachesExplained · google/guava Wiki (github.com)
This library is widely used by many companies in their projects.
This can be added to the project very easily using maven or gradle.
For Caching provides CacheBuilder
class to build caches like LoadingCache or Simple cache to store key-value pairs. With the Loading class, you can attach the loader function to load value for a key if that key has no value in cache or a value is evicted from the cache.
We can evict items based on time, size, and week reference. Refer following example to use with a basic case.
public class LoadingCacheExample {
// Use key and value of yor required type
private final CacheLoader<String, String> loader;
public LoadingCache<String, String> getCache() {
return cache;
}
private final LoadingCache<String, String> cache;
public LoadingCacheExample() {
loader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
// If not found in cached they can be loaded here
return key.toUpperCase();
}
};
cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(loader);
}
public void add(String key, String value) {
this.cache.put(key, value);
}
public String get(String key) throws ExecutionException {
return this.cache.get(key);
}
public static void main(String[] args) throws ExecutionException {
LoadingCacheExample cacheExample = new LoadingCacheExample();
cacheExample.add("k1", "Anupama");
System.out.println(cacheExample.get("k11"));
}
}
With LoadingCache you can also attach an eviction listener and refresh cache by keys.
If you do not want a loading function then you can simply use it as given below.
Cache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
Ehcache
Ehcache is an open-source, standards-based cache that boosts performance, offloads your database, and simplifies scalability. This can be configured based on XML config as well as using POJO class config objects.
This provides support of cache manager using that one can create and manage more than one cache with different configurations.
Simple Ehcache Example
public class EhcacheExample {
private CacheManager cacheManager;
private Cache<String, Integer> cache1;
private Cache<String, String> cache2;
public EhcacheExample() {
cacheManager = CacheManagerBuilder
.newCacheManagerBuilder().build();
cacheManager.init();
cache1 = cacheManager
.createCache("cache1", CacheConfigurationBuilder
.newCacheConfigurationBuilder(
String.class, Integer.class,
ResourcePoolsBuilder.heap(10)));
CacheConfigurationBuilder<String, String> configBuilder = CacheConfigurationBuilder
.newCacheConfigurationBuilder(
String.class, String.class,
ResourcePoolsBuilder.heap(10));
cache2 = cacheManager
.createCache("cache2", configBuilder);
}
public Cache<String, Integer> getCache1() {
return cacheManager.getCache("cache1", String.class, Integer.class);
}
public Cache<String, String> getCache2() {
return cacheManager.getCache("cache2", String.class, String.class);
}
public static void main(String[] args) {
EhcacheExample ehcacheExample = new EhcacheExample();
Cache<String, Integer> cache1 = ehcacheExample.getCache1();
Cache<String, String> cache2 = ehcacheExample.getCache2();
cache2.put("k1", "anupama");
}
}
This can be easily added to project using maven or gradle build.
Apache Commons Collection PassiveExpiringMap
Apache commons collection is very handy and useful lib, This also provides PassiveExpiringMap class which can be used to create in-memory cached with time based expiry of elements.
Example of PassiveExpiringMap
Map<String, Integer> chm = new ConcurrentHashMap<>();
PassiveExpiringMap.ExpirationPolicy<String, Integer> expirationPolicy = new ConstantTimeToLiveExpirationPolicy<>(
5, TimeUnit.SECONDS);
PassiveExpiringMap<String, Integer> expiringMap = new PassiveExpiringMap<String, Integer>(expirationPolicy, chm);
expiringMap.put("k1", 100);
System.out.println(expiringMap.get("k1"));
Thread.sleep(6000);
System.out.println(expiringMap.get("k1"));
Core Java Map Class
If your requirement is simple to store key-value in memory and does not want any other function like time-based eviction or automatic loading then you can use core java Map collections. If synchronization is required hen you can use a synchronized version of the map.
Map<K, V> myCache = Collections.synchronizedMap(new WeakHashMap<K, V>());
Apart from these, you can try cache2k – Java Caching lib. Or you can implement your own based on your requirement.