您仍然可以将 ConcurrentLinkedHashMap 与 Caffeine 一起使用吗?

Can you still use a ConcurrentLinkedHashMap with Caffeine?

Caffeine 为 Guava 的缓存库提供了一个很好的替代方案,但是您仍然可以仅使用 ConcurrentLinkedHashMap class 本身,或者实现并发、有序迭代的 Cache 吗?它不再出现在我能看到的 Caffeine 中,尽管我确信它在幕后使用了类似的东西。

我已经测试了 Caffeine Cache,它不会按插入顺序遍历元素,而且我在 Caffeine 构建器上看不到任何让我认为此功能仍然存在的选项。但也许我遗漏了什么。

有什么想法吗?

ConcurrentLinkedHashMap 通过升序和降序快照提供了 access-order 迭代。这对于为热服务器重启保留最热条目的情况很有用。否则视图和迭代器按底层 ConcurrentHashMap.

的顺序排列

Caffeine 按逐出策略的 hottest/coldest 顺序和过期策略的 youngest/oldest 顺序提供快照。参见 Cache.policy()

Insertion-order 实现起来并不困难,因为写入很少见、独占,并且项目不会经常移动。最简单的方法是使用 Map 计算来维护二级数据结构。如果除了缓存之外你还想要它,那么 Caffeine 的 asMap() 视图提供了原子版本。通过这种方式,您可以通过 ConcurrentHashMap 进行快速随机访问读取并为后台进程进行有序迭代。

var data = new ConcurrentHashMap<String, Integer>();
var order = Collections.synchronizedMap(new LinkedHashMap<String, Integer>());

data.compute("a", (key, oldValue) -> {
  order.put(key, 1);
  return 1;
});

// Locks the map during access, blocking writes; consider snapshotting first
order.forEach((key, value) -> System.out.printf("%s=%s%n", key, value);

Vavr 的 LinkedHashMap 如果您需要并发迭代或想避免完整副本的成本,可能会有用。这是一个持久的数据结构,写入会产生一个新的不可变版本。如果您不需要任何缓存概念,可以在上面或直接使用它。

volatile Map<K, V> data = LinkedHashMap.empty();
final Lock lock = new ReentrantLock();

// Single writer
lock.lock();
try {
  data = data.put(1, 2).put(3, 4);
} finally {
  lock.unlock();
}

// Multiple readers
System.out.println(data);