Memoizer
Cache the result of any pure function — Supplier, Function, BiFunction or TriFunction.
Memoizer wraps a pure function so subsequent calls with the same arguments are served from cache. The cache lives in a private ConcurrentHashMap per memoized function, so concurrent callers are safe.
The API
All four overloads live on com.svenruppert.functional.memoizer.Memoizer:
static <T> Supplier<T> memoize(Supplier<T> supplier);
static <T, U> Function<T, U> memoize(Function<T, U> function);
static <T1, T2, R> BiFunction<T1, T2, R> memoize(BiFunction<T1, T2, R> biFunc);
static <T1, T2, T3, R> TriFunction<T1, T2, T3, R> memoize(TriFunction<T1, T2, T3, R> triFunc);
Function: classic memoization
Function<Integer, Long> slowFib = Memoizer.memoize(n ->
n < 2 ? n : slowFib.apply(n - 1) + slowFib.apply(n - 2));
slowFib.apply(40); // fast — even though the body is naïve
The trick: the recursive lambda calls the memoized function (slowFib), not itself directly. Without memoization, fib(40) performs over a billion calls.
BiFunction & TriFunction: curry-based memoization
Bi- and TriFunctions internally curry into nested Functions and memoize each level, giving you partial caching as a bonus:
BiFunction<Integer, Integer, Integer> multiply = Memoizer.memoize((x, y) -> x * y);
multiply.apply(3, 4); // computed
multiply.apply(3, 4); // cached
multiply.apply(3, 5); // only the inner level recomputes (x=3 partial is reused)
TriFunction<Integer, Integer, Integer, Integer> abc = Memoizer.memoize((a, b, c) -> a * b + c);
Bridging legacy code
A method reference on an instance is just a BiFunction (or TriFunction) — so legacy code can be opportunistically memoized without touching it:
final MyLegacyClass legacy = new MyLegacyClass();
final BiFunction<Integer, Integer, Value> raw = legacy::doWork;
final BiFunction<Integer, Integer, Value> cached = Memoizer.memoize(raw);
The original method is untouched; cached adds caching for callers who want it.
Cache lifecycle
Each call to Memoizer.memoize(...) returns a new memoized function with its own private cache. Hold the returned reference for as long as you want the cache to live; let it go out of scope and the cache is GC’d along with it.
There is no built-in size limit, eviction, or weak-reference variant. If you need any of those, wrap your own Cache (Caffeine, Guava) and expose it as a Function.
Related
- Transformations —
curryBiFunction/curryTriFunctionare whatMemoizeruses internally. - Tutorial: Memoizing expensive computations.