Метод Java мемоизация
я столкнулся с интересной проблемой и задавался вопросом, Можно ли и как это сделать на Java: Создайте метод, который может запоминать любую функцию / метод . Этот метод имеет следующие аргументы : метод/функция и аргумент(ы) для этого.
например, скажем, у меня есть этот метод :
int addOne(int a) { return a + 1;}
и я вызываю свой метод memoization два раза с теми же аргументами: addOne и 5 например, первый вызов должен фактически вызвать метод addOne и вернуть результат, а также сохранить этот результат для данного аргумента. Во второй раз, когда я звоню, он должен знать, что это было вызвано раньше, и просто посмотрите предыдущий ответ.
моя идея состояла бы в том, чтобы иметь что-то вроде HashMap<Callable,HashMap<List<Objects>,Object>>
где вы будете хранить предыдущие ответы и искать их позже.Я думаю, что это можно как-то сделать с лямбда-выражениями, но я не так хорошо с ними знаком.Я не совсем уверен, как написать этот метод и был бы признателен за некоторую помощь.
может это можно сделать с помощью такого подхода?
2 ответов
в Java 8, вы можете сделать это так:
Map<Integer, Integer> cache = new ConcurrentHashMap<>();
Integer addOne(Integer x) {
return cache.computeIfAbsent(x -> x + 1);
}
это хороший учебник. Там он сделан для любого метода.
из учебника:
класс Memoizer:
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);
}
}
как использовать класс:
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);
}
выход:
2
2
1000
0
вы можете запомнить любую функцию с помощью Java 8 MethodHandle
s и lambdas, если вы готовы отказаться от безопасности типа по параметрам:
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);
}
});
};
}
это создает переменную-arity лямбда, которая заключает функцию и почти так же быстро, как вызов функции напрямую (т. е. внутри call(Object...args)
) после того, как лямбда построена, так как мы используем MethodHandle.invoke()
вместо Method.invoke()
.
вы все еще можете сделать это без lambdas (заменить анонимными классами) и MethodHandles (заменить методом.invoke), но будут штрафы за производительность, которые делают это менее привлекательным для кода, учитывающего производительность.