The title is very straightforward, I am trying to use caffeine cache for my Minecraft plugin and I can't seem to find any good examples for cache.getAll()
. The documentation doesn't help either. All I see is <
,>
, and ?
. I really think that documentation should provide examples. So all I'm asking is if someone could provide an example

- 357
- 5
- 14
-
When you build the caffeine cache, you are building a type of `Cache
` where K is the key type, and V is the value type. Any operation that accepts `? extends K` is telling that method accepts a type/subtype of K. – aksappy Jul 31 '21 at 00:02
2 Answers
Actually using the documentation you should be able to understand what it's expecting as arguments. The author did a very good job. If you don't, I'd suggest you to read on Java generics and understand the syntax.
TL;DR:
Cache<String, Integer> cache = Caffeine.newBuilder().build();
cache.getAll(List.of("one", "two", "three"), keys -> {
Map<String, Integer> result = new HashMap<>();
keys.forEach(key -> {
if (key.equals("one")) result.put(key, 1);
else if (key.equals("two")) result.put(key, 2);
else if (key.equals("three")) result.put(key, 3);
});
return result;
});
The example might not make much sense in terms of functionality, but it's an example.
The long version
The documentation has a method signature of:
Map<K,V> getAll(Iterable<? extends K> keys, Function<? super Set<? extends K>,? extends Map<? extends K,? extends V>> mappingFunction)
Let's break this down.
Map<K,V> getAll
The getAll
method returns a Map with a key of type K
and a value of type V
. If your cache implementation is something like:
Cache<String, Integer> cache = Caffeine.newBuilder().build();
then K == String
and V == Integer
The first argument of that method is:
Iterable<? extends K> keys
Now Iterable
is a JDK interface, which is implemented by pretty much all of the Collections. Check this for "known" implementing classes.
The question mark actually means "any type that extends K
" (where K
is String in the example)
So as a first argument we can use something like:
List.of("one", "two")
Function<? super Set<? extends K>,? extends Map<? extends K,? extends V>>
That might look confusing but it isn't if you try to break it down again. Function is an interface (that declares only one method, hence functional) that declares as the first type, the type of the input and as the second type, the type of the return. More info here.
So ? super Set<? extends K>
is any type that is a superset of Set
with elements of type K
(aka String in the example). Why the author opted for the super
keyword? Google what "PECS" is (producer extends, consumer super).
And then ? extends Map<? extends K,? extends V>
is as you have figured out, an implementation of Map
with keys of type K
and values of type V
(or Integer).
Notes
Returns a map of the values associated with the keys, creating or retrieving those values if necessary. The returned map contains entries that were already cached, combined with the newly loaded entries; it will never contain null keys or values.
Self explanatory but note that the returned map would have all cache values and your mappingFunction
's results combined - without null
keys/values
A single request to the mappingFunction is performed for all keys which are not already present in the cache
Your mappingFunction
will be called only once, with a list of all the keys that were requested but not found in the cache.
All entries returned by
mappingFunction
will be stored in the cache, over-writing any previously cached values
Self explanatory as well, the map that the mappingFunction
returns will replace or be stored in cache

- 435
- 6
- 16
The user-guide provides a simple examples of this functionality. A getAll
method is available in all flavors of the cache.
Synchronous cache
In this abstraction, the caller will block waiting for the loads to complete
Cache<Key, Graph> cache = Caffeine.newBuilder().build();
cache.put(k1, v1);
// Loads k2 & k3 (uses k1)
Map<Key, Graph> graphs = cache.getAll(Set.of(k1, k2, k3),
keys -> createExpensiveGraphs(keys));
If you wish to decouple the caller from the loading function, then create a LoadingCache
. If the supplied CacheLoader
does not implement bulk functionality, then it will fallback to one-by-one loading.
LoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.build(CacheLoader.bulk(keys -> createExpensiveGraphs(keys)));
Map<Key, Graph> graphs = cache.getAll(keys);
Note that in this abstraction loads are non-blocking. If a getAll
is retrieving k2
and a blocking get(k2)
occurs concurrently then both loads will be in-flight. When the getAll
completes it will overwrite the existing value. This behavior is because we cannot lock multiple entries in ConcurrentHashMap
compute methods, but that concern is resolved if using an AsyncCache
.
Asynchronous Cache
In this abstraction the cache stores a mapping to a CompletableFuture
and returns that to the caller. You can either call with a synchronous function that wraps as asynchronous, or use an asynchronous function that returns the futures directly.
AsyncCache<Key, Graph> cache = Caffeine.newBuilder().buildAsync();
CompletableFuture<Map<Key, Graph>> graphs1 = cache.getAll(Set.of(k1, k2, k3),
keys -> createExpensiveGraphs(keys));
CompletableFuture<Map<Key, Graph>> graphs2 = cache.getAll(Set.of(k1, k2, k3),
(keys, executor) -> createExpensiveGraphFutures(keys));
The loading logic can be hidden from the caller using an AsyncLoadingCache
.
AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.build(AsyncCacheLoader.bulk(keys -> createExpensiveGraphs(keys)));
CompletableFuture<Map<Key, Graph>> graphs = cache.getAll(keys);
In this abstraction getAll
will block subsequent get
calls for the same key. This is due to the underlying map holding an in-flight future entry, so placeholders are inserted and completed when the bulk operation is done.

- 9,178
- 3
- 35
- 39