Here is a situation. I have a non blocking stream of String
. These String
s can repeat so I want to setup a cache that keeps the record of last 10 seconds of String
that were recieved. After enabling the cache the system's network has improved drastically but at the cost of performance. Here is what I am using to cache:
@Getter
@ToString
class StringWrapper implements Comparable<StringWrapper> {
private String string;
private long time;
private StringWrapper() {}
public StringWrapper(String string) {
this();
this.string = string;
time = System.currentTimeMillis();
}
@Override
public int compareTo(StringWrapper o) {
return getTime() > o.getTime() ? 1 : getTime() < o.getTime() ? -1: 0;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof StringWrapper)) { throw new IllegalArgumentException(); }
StringWrapper sw = (StringWrapper) obj;
return string.equals(sw.getString());
}
}
class SetProxy extends HashSet<StringWrapper> {
private interface Thunk { public void exec(); }
// expire by ten second
private final Thunk thunk = () -> removeIf(sw -> System.currentTimeMillis() - sw.getTime() > 1000 * 10);
@Override
public boolean add(StringWrapper e) {
thunk.exec();
return super.add(e);
}
//... similar method overrides that invoke thunk internally
}
I have to invoke thunk.exec()
on each method that add or removes from the Set
otherwise the memory consumption will increase. The problem is with removeIf
inside thunk#exec
, it iterates over the set again and again.
I thought of having a seperate scheduler that executes every one second to remove the expired String
but it comes along with concurrency problems.
Can someone suggest a better workaround?