1

标题非常简单,我正在尝试为我的 Minecraft 插件使用咖啡因缓存,但我似乎找不到任何好的示例cache.getAll()。该文档也无济于事。我只看到<,>?. 我真的认为文​​档应该提供示例。所以我要问的是是否有人可以提供一个例子

4

2 回答 2

3

实际上使用文档,您应该能够理解它期望的参数。作者做得非常好。如果你不这样做,我建议你阅读 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 == StringV == Integer

该方法的第一个参数是:

可迭代<? 扩展 K> 键

现在Iterable是一个 JDK 接口,几乎所有的 Collections 都实现了它。检查“已知”实现类。问号实际上表示“任何扩展的类型K”(K示例中的字符串在哪里)

因此,作为第一个参数,我们可以使用如下内容:

List.of("one", "two")

函数<? 超集<? 扩展 K>,​? 扩展地图<? 扩展 K,​? 延伸V>>

这可能看起来令人困惑,但如果您再次尝试将其分解,则不会。函数是一个接口(仅声明一个方法,因此是函数式的),它声明为第一种类型,即输入的类型,以及第二种类型,即返回的类型。更多信息在这里

任何作为类型元素? super Set<? extends K>超集的类型(在示例中为字符串)也是如此。为什么作者选择关键字?谷歌什么是“PECS”(生产者扩展,消费者超级)。SetKsuper

然后​? extends Map<? extends K,​? extends V>正如您所知道的,Map使用类型键和类型(或整数)K值的实现。V

笔记

返回与键关联的值的映射,必要时创建或检索这些值。返回的地图包含已经缓存的条目,以及新加载的条目;它永远不会包含空键或值。

不言自明,但请注意,返回的地图将包含所有缓存值和您mappingFunction的结果组合 - 没有null键/值

对缓存中不存在的所有键执行对 mappingFunction 的单个请求

mappingFunction将只被调用一次,并列出所有已请求但未在缓存中找到的键。

返回的所有条目mappingFunction都将存储在缓存中,覆盖任何以前缓存的值

同样不言自明,mappingFunction返回的地图将替换或存储在缓存中

于 2021-07-31T02:02:26.370 回答
1

用户指南提供了此功能的简单示例。一种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对同一个键的后续调用。这是由于底层映射包含一个正在进行的未来条目,因此当批量操作完成时会插入并完成占位符。

于 2021-07-31T02:01:05.727 回答