24

I know of this one http://onjava.com/pub/a/onjava/2003/08/20/memoization.html but is there anything else?

mtk
  • 13,221
  • 16
  • 72
  • 112
ranv01
  • 259
  • 1
  • 2
  • 5
  • 1
    This example does memoization on all methods of an object via a proxy. But typical memoization is one function at the time. That proxy technique would be annoying when you dont want to memoize all the methods of an object. – lacroix1547 Sep 02 '10 at 05:45

3 Answers3

23

To memoize functions without parameters, use Guava's Suppliers.memoize(Supplier). For functions with parameters, use CacheBuilder.build(CacheLoader) with parameter value objects as keys.

Vadzim
  • 24,954
  • 11
  • 143
  • 151
thSoft
  • 21,755
  • 5
  • 88
  • 103
17

Yes. Use caches from Guava.

Example:

import java.math.BigInteger;

import com.google.common.base.Preconditions;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

public class Fibonacci {
    private static final LoadingCache<Integer, BigInteger> CACHE
            = CacheBuilder.newBuilder().build(CacheLoader.from(Fibonacci::fib));

    public static BigInteger fib(int n) {
        Preconditions.checkArgument(n >= 0);
        switch (n) {
        case 0:
            return BigInteger.ZERO;
        case 1:
            return BigInteger.ONE;
        default:
            return CACHE.getUnchecked(n - 1).add(CACHE.getUnchecked(n - 2));
        }
    }
}
C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • 7
    MapMaker is now deprecated in favour of CacheBuilder: https://code.google.com/p/guava-libraries/wiki/MapMakerMigration – dzieciou Nov 08 '13 at 19:55
  • 2
    @dzieciou I've finally updated the code to something that works with the latest Guava (18.0 at current time of writing). And this time, it's tested! – C. K. Young Mar 22 '15 at 05:35
15

Memoization is also easy with plain simple typesafe Java.

You can do it from scratch with the following reusable classes.

I use these as caches whose lifespan are the request on a webapp.

Of course use the Guava MapMaker if you need an eviction strategy or more features like synchronization.

If you need to memoize a method with many parameters, just put the parameters in a list with both techniques, and pass that list as the single parameter.

abstract public class Memoize0<V> {
    //the memory
    private V value;
    public V get() {
        if (value == null) {
            value = calc();
        }
        return value;
    }
    /**
     * will implement the calculation that 
     * is to be remembered thanks to this class
     */
    public abstract V calc();
}

abstract public class Memoize1<P, V> {
    //The memory, it maps one calculation parameter to one calculation result
    private Map<P, V> values = new HashMap<P, V>();

    public V get(P p) {
        if (!values.containsKey(p)) {
            values.put(p, calc(p));
        }
        return values.get(p);
    }

    /**
     * Will implement the calculations that are
     * to be remembered thanks to this class
     * (one calculation per distinct parameter)
     */
    public abstract V calc(P p);
 }

And this is used like this

    Memoize0<String> configProvider = new Memoize0<String>() {
        @Override
        public String calc() {
            return fetchConfigFromVerySlowDatabase();
        }
    };
    final String config = configProvider.get();

    Memoize1<Long, String> usernameProvider = new Memoize1<Long, String>() {
        @Override
        public String calc(Long id) {
            return fetchUsernameFromVerySlowDatabase(id);
        }
    };
    final String username = usernameProvider.get(123L);
NickAldwin
  • 11,584
  • 12
  • 52
  • 67
lacroix1547
  • 1,060
  • 1
  • 8
  • 16
  • Guava is not approved yet for our environment, financial software... – ranv01 Sep 08 '10 at 01:08
  • Guava is not approved for our environment yet. Banking software... But this will do. I will limit the size of the map however to avoid memory leaks. I dont care about evictions since this will be conserved only during the invocation of one method. – ranv01 Sep 08 '10 at 02:31
  • 45
    I like the way highly tested code is not approved, but something pasted on SO is :) – Rob Grant May 19 '14 at 06:39
  • 1
    Be cautious using the Memoize0 example since it's not thread-safe. Multiples threads may call the calc() method numerous time in a race-condition. The Guava is implementation is thread-safe, lock-free on get() once initialized because it uses double-check locking : https://github.com/google/guava/blob/master/guava/src/com/google/common/base/Suppliers.java#L114 – Daniel Marcotte Aug 21 '15 at 15:22
  • It looks like `MapMaker` doesn't exist (anymore). Try [CacheBuilder](https://github.com/google/guava/wiki/CachesExplained) instead. – jpaugh Jul 25 '16 at 21:16
  • @RobertGrant Not surprising to me. Guava is big, while something tiny and purpose-built like this, using only stuff built in to Java, is easy to verify. – Izkata Aug 19 '18 at 06:19