Java memoization method

I ran into an interesting problem and wondered how and how to do this in Java: Create a method that can memoize any function / method. A method has the following arguments: a method / function and an argument for it.

For example, let's say I have this method:

int addOne(int a) { return a + 1;}

and I call the memoization method twice with the same arguments: for example, addOne and 5, the first call should actually call the addOne method and return the result, and also save this result for the given argument. The second time I call this, I should know that it was called earlier and just looked at the previous answer.

My idea would be to have something like HashMap<Callable,HashMap<List<Objects>,Object>>where you would save the previous answers and look at them later. I think this can be somehow done with lambda expressions, but I am not familiar with them. "Not quite sure how to write this method, and would appreciate help."

Can this be done with this approach?

+4
source share
2 answers

In Java 8, you can do it like this:

Map<Integer, Integer> cache = new ConcurrentHashMap<>();

Integer addOne(Integer x) {
    return cache.computeIfAbsent(x -> x + 1);
}

This is a good tutorial. There it is created for any method.

From the textbook:

Memoizer Class:

public class Memoizer<T, U> {
    private final Map<T, U> cache = new ConcurrentHashMap<>();

    private Memoizer() {}
    private Function<T, U> doMemoize(final Function<T, U> function) {
        return input -> cache.computeIfAbsent(input, function::apply);
    }

    public static <T, U> Function<T, U> memoize(final Function<T, U> function) {
        return new Memoizer<T, U>().doMemoize(function);
    }
}

How to use the class:

Integer longCalculation(Integer x) {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException ignored) {
    }
    return x * 2;
}
Function<Integer, Integer> f = this::longCalculation;
Function<Integer, Integer> g = Memoizer.memoize(f);

public void automaticMemoizationExample() {
    long startTime = System.currentTimeMillis();
    Integer result1 = g.apply(1);
    long time1 = System.currentTimeMillis() - startTime;
    startTime = System.currentTimeMillis();
    Integer result2 = g.apply(1);
    long time2 = System.currentTimeMillis() - startTime;
    System.out.println(result1);
    System.out.println(result2);
    System.out.println(time1);
    System.out.println(time2);
}

Conclusion:

2
2
1000
0
+9
source

You can memoize any function with Java 8 MethodHandleand lambdas if you are ready to give up type safety by parameters:

public interface MemoizedFunction<V> {
    V call(Object... args);
}

private static class ArgList {
    public Object[] args;

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof ArgList)) {
            return false;
        }

        ArgList argList = (ArgList) o;

        // Probably incorrect - comparing Object[] arrays with Arrays.equals
        return Arrays.equals(args, argList.args);
    }

    @Override
    public int hashCode() {
        return args != null ? Arrays.hashCode(args) : 0;
    }
}

public static <V> MemoizedFunction<V> memoizeFunction(Class<? super V> returnType, Method method) throws
                                                                                                  IllegalAccessException {
    final Map<ArgList, V> memoizedCalls = new HashMap<>();
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    MethodHandle methodHandle = lookup.unreflect(method)
                                      .asSpreader(Object[].class, method.getParameterCount());
    return args -> {
        ArgList argList = new ArgList();
        argList.args = args;
        return memoizedCalls.computeIfAbsent(argList, argList2 -> {
            try {
                //noinspection unchecked
                return (V) methodHandle.invoke(args);
            } catch (Throwable throwable) {
                throw new RuntimeException(throwable);
            }
        });
    };
}

Working example

lambda arity, , (.. call(Object...args)) , MethodHandle.invoke() Method.invoke().

lambdas ( ) MethodHandles ( Method.invoke), , .

+2

Source: https://habr.com/ru/post/1568171/


All Articles