java Java记忆方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27549864/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 11:58:34  来源:igfitidea点击:

Java memoization method

javalambdamemoization

提问by Alex

I came across an interesting problem and was wondering if and how could this be done in Java: Create a method which can memoize any function/method . The method has the following arguments : the method/function and the argument(s) for it.

我遇到了一个有趣的问题,想知道在 Java 中是否以及如何做到这一点:创建一个可以记忆任何函数/方法的方法。该方法具有以下参数:方法/函数及其参数。

For example let's say i have this method :

例如,假设我有这个方法:

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

and i call my memoization method two times with the same arguments : addOne and 5 for example, the first call should actually call the addOne method and return the result and also store that result for that given argument. The second time when i call it should know this has been called before and just look up the previous answer.

我用相同的参数调用了我的记忆方法两次:例如,addOne 和 5,第一次调用实际上应该调用 addOne 方法并返回结果并存储该给定参数的结果。当我第二次调用它时,应该知道之前已经调用过它,只需查找上一个答案即可。

My idea would be to have something like a HashMap<Callable,HashMap<List<Objects>,Object>>where you would store the previous answers and look them up later on.I think this can be somehow done with lambda expressions but i'm not that familiar with them.I'm not quite sure how to write this method and would appreciate some help.

我的想法是有一个类似的东西HashMap<Callable,HashMap<List<Objects>,Object>>,你可以存储以前的答案并在以后查找它们。我认为这可以用 lambda 表达式来完成,但我对它们不太熟悉。我不太确定如何编写此方法并希望得到一些帮助。

Can this be done with this approach?

这可以用这种方法完成吗?

回答by code monkey

In Java 8 you can do it like that:

在 Java 8 中,你可以这样做:

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

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

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

这是一个很好的教程。它适用于任何方法。

From the tutorial:

从教程:

The Memoizer class:

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);
    }
}

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);
}

Output:

输出:

2
2
1000
0

回答by Darth Android

You can memoize any function with Java 8's MethodHandles and lambdas if you're willing to give up type safety on the parameters:

MethodHandle如果您愿意放弃参数的类型安全,您可以使用 Java 8 的s 和 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);
            }
        });
    };
}

Working Example

工作示例

This creates a variable-arity lambda that encloses the function and is almost as fast as calling the function directly (i.e., no reflection happens inside of call(Object...args)) after the lambda is constructed since we're using MethodHandle.invoke()instead of Method.invoke().

这将创建一个包含函数的可变参数 lambda,并且call(Object...args)在构造 lambda 之后几乎与直接调用函数一样快(即,内部没有发生反射),因为我们使用的是MethodHandle.invoke()代替Method.invoke()

You can still do this without lambdas (replace with anonymous classes) and MethodHandles (replace with Method.invoke), but there will be performance penalties that make this less attractive for performance-conscious code.

您仍然可以在没有 lambdas(用匿名类替换)和 MethodHandles(用 Method.invoke 替换)的情况下执行此操作,但是会有性能损失,这使得这对注重性能的代码不那么有吸引力。