java - ConcurrentHashMap:按条件删除
问题描述
我有一个ConcurrentHashMap
用作内存存储(或缓存,你可能会说)
我想要实现的是:同时检查一个项目是否“准备好”,如果是,则将其从地图中删除(+将其返回给调用者)。没有直接的方法可以让我做到这一点。
我想出的唯一解决方案是ItemContainer
包含项目和元数据(isReady
字段)。在每次访问时,我都必须申请merge
或compute
操作。基本上在每次访问/检查时替换对象的容器。
问题:
- 我的解决方案看起来合理吗?
- 有没有什么好的库可以实现类似的东西?
我按要求添加了“样板”代码:
public class Main {
public static void main(String[] args) {
Storage storage = new Storage();
storage.put("1", new Record("s", 100));
storage.put("2", new Record("s", 4));
storage.removeIf("1", Main::isReady);
}
public static boolean isReady(Record record) {
return record.i > 42;
}
public static class Record {
public Record(String s, Integer i) {
this.s = s;
this.i = i;
}
String s;
Integer i;
}
public static class Storage {
ConcurrentHashMap<String, Record> storage = new ConcurrentHashMap<>();
public void put(String key, Record record) {
storage.put(key, record);
}
public Record removeIf(String key, Function<Record, Boolean> condition) {
return null; // TODO: implement
}
}
}
其他解决方案(权衡):
- 始终
remove()
检查,然后merge()
返回地图。 - 使用具有一些合理的项目疏散策略(即 LRU)的缓存并仅检查已疏散的项目。
基于@ernest_k 解决方案:
public Record removeIf(String key, Predicate<Record> condition) {
AtomicReference<Record> existing = new AtomicReference<>();
this.storage.computeIfPresent(key, (k, v) -> {
boolean conditionSatisfied = condition.test(v);
if (conditionSatisfied) {
existing.set(v);
return null;
} else {
existing.set(null);
return v;
}
});
return existing.get();
}
解决方案
ConcurrentHashMap
已经为您提供了原子性的保证computeIfPresent
。
如果指定键的值存在,则尝试在给定键及其当前映射值的情况下计算新映射。整个方法调用以原子方式执行。在计算过程中,其他线程对该映射的某些尝试更新操作可能会被阻塞,因此计算应该简短而简单,并且不得尝试更新该映射的任何其他映射。
所以你可以使用它:
public Record removeIf(String key, Predicate<Record> condition) {
AtomicReference<Record> existing = new AtomicReference<>();
this.storage.computeIfPresent(key, (k, v) -> {
existing.set(v);
return condition.test(v) ? null : v;
});
return existing.get();
}
请注意,我使用Predicate<Record>
它应该优先于Function<Record, Boolean>
.
将当前值存储在AtomicReference
此处的原因是确保返回的值与测试谓词的值相同(否则可能存在竞争条件)。
推荐阅读
- c# - 如何在 C# 中将位图压缩为更小的尺寸?
- python - 无法按收到日期的顺序从 Outlook 中的某个文件夹获取邮件
- c - 我可以更改以前在 C 中声明的变量的数据类型吗?
- c# - 在 .NET Core 中使用环境变量指定布尔变量
- android - Android Automotive 如何检索 obd2 数据?
- drupal - 如何在 Drupal 8 中显示最新文章标题?
- mysql - 使用两个 SELECTS 而不是四个更新临时表列
- json - PostgreSQL 将 JSONB 列转储到一行 JSON 文件中
- python - 使用 numpy 替换两个索引之间的数组值
- angular - Angular PrimeNG Inputswitch 显示段落