标题非常简单,我正在尝试为我的 Minecraft 插件使用咖啡因缓存,但我似乎找不到任何好的示例cache.getAll()
。该文档也无济于事。我只看到<
,>
和?
. 我真的认为文档应该提供示例。所以我要问的是是否有人可以提供一个例子
2 回答
实际上使用文档,您应该能够理解它期望的参数。作者做得非常好。如果你不这样做,我建议你阅读 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<K,V> getAll(Iterable<? extends K> keys, Function<? super Set<? extends K>,? extends Map<? extends K,? extends V>> mappingFunction)
让我们分解一下。
地图<K,V> getAll</p>
该getAll
方法返回一个带有 type 键和 typeK
值的Map V
。如果您的缓存实现类似于:
Cache<String, Integer> cache = Caffeine.newBuilder().build();
然后K == String
和V == Integer
该方法的第一个参数是:
可迭代<? 扩展 K> 键
现在Iterable
是一个 JDK 接口,几乎所有的 Collections 都实现了它。检查此“已知”实现类。问号实际上表示“任何扩展的类型K
”(K
示例中的字符串在哪里)
因此,作为第一个参数,我们可以使用如下内容:
List.of("one", "two")
函数<? 超集<? 扩展 K>,? 扩展地图<? 扩展 K,? 延伸V>>
这可能看起来令人困惑,但如果您再次尝试将其分解,则不会。函数是一个接口(仅声明一个方法,因此是函数式的),它声明为第一种类型,即输入的类型,以及第二种类型,即返回的类型。更多信息在这里。
任何作为类型元素? super Set<? extends K>
超集的类型(在示例中为字符串)也是如此。为什么作者选择关键字?谷歌什么是“PECS”(生产者扩展,消费者超级)。Set
K
super
然后? extends Map<? extends K,? extends V>
正如您所知道的,Map
使用类型键和类型(或整数)K
值的实现。V
笔记
返回与键关联的值的映射,必要时创建或检索这些值。返回的地图包含已经缓存的条目,以及新加载的条目;它永远不会包含空键或值。
不言自明,但请注意,返回的地图将包含所有缓存值和您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);
请注意,在此抽象中,负载是非阻塞的。如果 agetAll
正在检索并且同时发生k2
阻塞,则两个加载都将在进行中。get(k2)
完成getAll
后,它将覆盖现有值。这种行为是因为我们不能在ConcurrentHashMap
计算方法中锁定多个条目,但是如果使用AsyncCache
.
异步缓存
在这个抽象中,缓存存储到 a 的映射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
对同一个键的后续调用。这是由于底层映射包含一个正在进行的未来条目,因此当批量操作完成时会插入并完成占位符。