Ale*_*lex 12 java lambda memoization
我遇到了一个有趣的问题,并想知道是否以及如何在Java中完成:创建一个可以记住任何函数/方法的方法.该方法具有以下参数:方法/函数及其参数.
例如,假设我有这种方法:
int addOne(int a) { return a + 1;}
Run Code Online (Sandbox Code Playgroud)
并且我使用相同的参数调用我的memoization方法两次:例如addOne和5,第一个调用应该实际调用addOne方法并返回结果并且还存储该给定参数的结果.第二次我打电话的时候应该知道之前已经打过电话,只是查看上一个答案.
我的想法是有一个类似于HashMap<Callable,HashMap<List<Objects>,Object>>你将存储以前的答案并在以后查找它们的地方.我认为这可以用lambda表达式以某种方式完成但我不熟悉它们.我不太确定写这个方法,并希望得到一些帮助.
这可以通过这种方法完成吗?
cod*_*key 19
在Java 8中,您可以这样做:
Map<Integer, Integer> cache = new ConcurrentHashMap<>();
Integer addOne(Integer x) {
return cache.computeIfAbsent(x -> x + 1);
}
Run Code Online (Sandbox Code Playgroud)
从教程:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
如何使用课程:
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);
}
Run Code Online (Sandbox Code Playgroud)
输出:
2
2
1000
0
Run Code Online (Sandbox Code Playgroud)
MethodHandle如果你愿意放弃参数的类型安全性,你可以用Java 8 和lambda 来记忆任何函数:
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);
}
});
};
}
Run Code Online (Sandbox Code Playgroud)
这创建了一个包含函数的变量lambda,几乎与call(Object...args)在构造lambda之后直接调用函数(即,内部没有反射)一样快,因为我们使用MethodHandle.invoke()而不是Method.invoke().
你仍然可以在没有lambdas(替换为匿名类)和MethodHandles(替换为Method.invoke)的情况下执行此操作,但是会有性能损失使得这对性能敏感的代码不那么有吸引力.