Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Data structure for caching computation result?

I have an expensive computation, the result of which I'd like to cache. Is there some way to make a map with two keys? I'm thinking of something like Map<(Thing1, Thing2), Integer>.

Then I could check:

if (! cache.contains(thing1, thing2)) {
  return computeResult();
}
else {
  return cache.getValue(thing1, thing2);
}

pseudocode. But something along those lines.

like image 875
Nick Heiner Avatar asked Aug 07 '26 20:08

Nick Heiner


2 Answers

You need to create a class that holds Thing1 and Thing2, e.g:

class Things {
    public final Thing1 thing1;
    public final Thing2 thing2;
    public Things(Thing1 thing1, Thing2 thing2) {
      this.thing1 = thing1; 
      this.thing2 = thing2;
    }
    @Override
    public boolean equals(Object obj) { ... }
    @Override
    public int hashCode() { ... };
 }

Then to use it:

Things key = new Things(thing1, thing2);
if (!cache.contains(key) {
    Integer result = computeResult();
    cache.put(key, result);
    return result;
} else {
    return cache.getValue(key);
}

Note you have to implement equals and hashcode to make this code work properly. If you need this code to be thread-safe then have a look at ConcurrentHashMap.

like image 168
Dean Povey Avatar answered Aug 10 '26 10:08

Dean Povey


Sounds like you want memoisation. The latest trunk head of Functional Java has a memoising product type P1 that models a computation whose result is cached.

You would use it like this:

P1<Thing> myThing = new P1<Thing>() {
  public Thing _1() {
    return expensiveComputation();
  }
}.memo();

Calling _1() the first time will run the expensive computation and store it in the memo. After that, the memo is returned instead.

For your "two keys", you'd want a simple pair type. Functional Java has this too in the form of the class P2<A, B>. To memoise such a value, simply use P1<P2<A, B>>.

You can also use the Promise<A> class instead of the memoisation. This has been in the library for a while, so you'd just need the latest binary. You would use that as follows:

Promise<Thing> myThing =
  parModule(sequentialStrategy).promise(new P1<Thing>() {
    public Thing _1() {
      return expensiveComputation();
    }
  });

To get the result out, simply call myThing.claim(). Promise<A> also provides methods for mapping functions over the result even if the result is not yet ready.

You need to import static fj.control.parallel.ParModule.parModule and fj.control.parallel.Strategy.sequentialStrategy. If you want the computation to run in its own thread, replace sequentialStrategy with one of the other strategies provided by the Strategy class.

like image 39
Apocalisp Avatar answered Aug 10 '26 10:08

Apocalisp



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!