I have Caffeine cache with Key->Value mapping. There are multiple implementations of Key
interface with different equals
methods. In order to delete value from cache based on someOtherVal
, I had to use code like cache.asMap().keySet().removeIf(comp::isSame)
which is super slow.
Is there any other solution for this kind of many keys to single value mapping in cache? One thing that comes to my mind is to have 2 Cache instances, one with Cache<Key, String>
and other with Cache<someOtherVal, Key>
, and whenever I want to delete a value I locate Key using this other cache.
Then only question is how to keep this 2 caches in sync? Are there already solutions for this?
import java.time.Duration;
import java.util.Objects;
import java.util.UUID;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.base.Stopwatch;
public class Removal {
private static final int MAX = 1_000_000;
interface Key{
String getSomeOtherVal();
default boolean isSame(Key k){
return Objects.equals(k.getSomeOtherVal(),getSomeOtherVal());
}
}
static class KeyImpl implements Key{
int id;
String someOtherVal;
public KeyImpl(int id, String someOtherVal) {
this.id = id;
this.someOtherVal = someOtherVal;
}
public int getId() {
return id;
}
@Override
public String getSomeOtherVal() {
return someOtherVal;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
KeyImpl key = (KeyImpl)o;
return id == key.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
Cache<Key, String> cache = Caffeine.newBuilder().build();
public static void main(String[] args) {
Removal s = new Removal();
s.fill();
Duration sRem = s.slowRemovalFirst100();
Duration fRem = s.fastRemoval100To200();
System.out.println("Slow removal in " + sRem);
System.out.println("Fast removal in " + fRem);
}
private Duration slowRemovalFirst100(){
Stopwatch sw = Stopwatch.createStarted();
for(int i=0; i<100; i++){
Key comp = new KeyImpl(i, String.valueOf(i));
cache.asMap().keySet().removeIf(comp::isSame); //Finds a key by some other property and then removes it (SLOW)
//System.out.println("Removed " + i);
}
return sw.stop().elapsed();
}
private Duration fastRemoval100To200(){
Stopwatch sw = Stopwatch.createStarted();
for(int i=100; i<200; i++){
Key comp = new KeyImpl(i, String.valueOf(i));
cache.invalidate(comp); //Uses direct access to map by key (FAST)
//System.out.println("Removed " + i);
}
return sw.stop().elapsed();
}
private void fill(){
for(int i=0; i<MAX; i++){
cache.put(new KeyImpl(i, String.valueOf(i)), UUID.randomUUID().toString());
}
}
}
Result of running this code on my machine:
Slow removal in PT2.807105177S
Fast removal in PT0.000126183S
where you can see such a big difference...