У меня есть 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();
}