标题非常简单,我试图使用咖啡因缓存我的Minecraft插件,我似乎找不到cache.getAll()
的任何好例子。文档也没有帮助。我只看到<
,>
和?
。我真的认为文档应该提供示例。所以我想问的是是否有人可以提供一个例子
实际上使用文档,您应该能够理解它期望作为参数的内容。作者写得很好。如果你不知道,我建议你阅读Java泛型并理解语法。
TL;博士:
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;
});
这个例子可能在功能方面没有多大意义,但它是一个例子。
长版本
文档有一个方法签名:
Map
getAll (Iterable, ?扩展Map
让我们来分析一下。
Map
getAll
getAll
方法返回一个键类型为K
,值类型为V
的Map。如果你的缓存实现是这样的:
Cache<String, Integer> cache = Caffeine.newBuilder().build();
thenK == String
andV == Integer
方法的第一个参数是:
Iterable
现在Iterable
是一个JDK接口,几乎所有的集合都实现了它。检查"已知"字样。实现类。问号实际上表示"任何扩展了K
的类型"。(其中K
在示例中为String)
作为第一个参数,我们可以这样使用:
List.of("one", "two")
Function, ?扩展Map
这可能看起来令人困惑,但如果你试图再次分解它,它就不会。Function是一个接口(它只声明一个方法,因此是函数式的),它声明第一个类型是输入的类型,第二个类型是返回的类型。更多信息在这里。
因此,? super Set<? extends K>
是Set
的超集,其元素类型为K
(在本例中称为String)。为什么作者选择了super
关键字?谷歌一下"PECS"是什么(生产者扩展,消费者超级)。
然后? extends Map<? extends K,? extends V>
是Map
的实现,键类型为K
,值类型为V
(或Integer)。
返回与键关联的值的映射,必要时创建或检索这些值。返回的映射包含已经缓存的条目,以及新加载的条目;它永远不会包含空键或空值。
不言自明,但请注意,返回的映射将包含所有缓存值和mappingFunction
的结果组合-没有null
的键/值
对缓存中未存在的所有键执行一次对mappingFunction的请求
您的mappingFunction
将只被调用一次,并带有所有被请求但在缓存中未找到的键的列表。
mappingFunction
返回的所有表项都将存储在缓存中,覆盖之前缓存的值
不言自明,mappingFunction
返回的映射将替换或存储在缓存
用户指南提供了该功能的一个简单示例。getAll
方法在所有类型的缓存中都可用。
在这个抽象中,调用者将阻塞等待加载完成
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));
如果您希望将调用者与加载函数解耦,则创建LoadingCache
。如果提供的CacheLoader
没有实现批量功能,那么它将退回到逐个加载。
LoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.build(CacheLoader.bulk(keys -> createExpensiveGraphs(keys)));
Map<Key, Graph> graphs = cache.getAll(keys);
注意,在这个抽象中,负载是非阻塞的。如果getAll
正在检索k2
,并且阻塞get(k2)
同时发生,则两个负载都将在飞行中。当getAll
完成时,它将覆盖现有的值。这种行为是因为我们不能在ConcurrentHashMap
计算方法中锁定多个条目,但是如果使用AsyncCache
,这个问题就解决了。
在这个抽象中,缓存存储到CompletableFuture
的映射并将其返回给调用者。你可以使用封装为异步的同步函数来调用,也可以使用直接返回期货的异步函数来调用。
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));
加载逻辑可以使用AsyncLoadingCache
对调用者隐藏。
AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.build(AsyncCacheLoader.bulk(keys -> createExpensiveGraphs(keys)));
CompletableFuture<Map<Key, Graph>> graphs = cache.getAll(keys);
在这个抽象中,getAll
将阻止后续对同一键的get
调用。这是由于底层映射持有一个正在运行的未来条目,因此在批量操作完成时插入占位符并完成占位符。