既然你已经重新修改了问题,这里是一个新的答案。
首先,我觉得你的 computeNonRecursivelly
实现仍然是递归的,因为它调用了 getOrEnqueue
。
我认为你不能直接使用 Cache
,因为你需要在计算中有两个步骤:第一个步骤陈述所需值的依赖关系,第二个步骤在满足依赖关系后进行计算。但是,如果出现循环依赖,则无法正常工作(与递归相同的要求)。
这样,你可以将未在缓存中的依赖项(及其依赖项等)排队,然后按正确顺序进行计算。大致如下:
public abstract class TwoStepCacheLoader<K, V> extends CacheLoader<K, V> {
public abstract Set<K> getDependencies(K key);
}
public class TwoStepCache<K, V> extends ForwardingLoadingCache<K, V> {
private final TwoStepCacheLoader<K, V> loader;
private LoadingCache<K, V> cache;
public TwoStepCache(TwoStepCacheLoader<K, V> loader) {
this.loader = loader;
cache = CacheBuilder.newBuilder().build(loader);
}
@Override
public V get(K key)
throws ExecutionException {
V value = cache.getIfPresent(key);
if (value != null) {
return value;
}
Deque<K> toCompute = getDependenciesToCompute(key);
return computeDependencies(toCompute);
}
private Deque<K> getDependenciesToCompute(K key) {
Set<K> seen = Sets.newHashSet(key);
Deque<K> dependencies = new ArrayDeque<K>(seen), toCompute = new ArrayDeque<K>(seen);
do {
for (K dependency : loader.getDependencies(dependencies.remove())) {
if (seen.add(dependency) &&
cache.getIfPresent(dependency) == null) {
toCompute.push(dependency);
dependencies.add(dependency);
}
}
} while (!dependencies.isEmpty());
return toCompute;
}
private V computeDependencies(Deque<K> toCompute)
throws ExecutionException {
V value;
do {
value = cache.get(toCompute.pop());
} while (!toCompute.isEmpty());
return value;
}
@Override
public V getUnchecked(K key) {
try {
return get(key);
} catch (ExecutionException e) {
throw new UncheckedExecutionException(e.getCause());
}
}
@Override
protected LoadingCache<K, V> delegate() {
return cache;
}
}
现在您可以实现一个
TwoStepCacheLoader
,安全地调用缓存:
public class Fibonacci {
private LoadingCache<Integer, Integer> cache = new TwoStepCache<Integer, Integer>(new FibonacciCacheLoader());
public int fibonacci(int n) {
return cache.getUnchecked(n);
}
private class FibonacciCacheLoader extends TwoStepCacheLoader<Integer, Integer> {
@Override
public Set<Integer> getDependencies(Integer key) {
if (key <= 1) {
return ImmutableSet.of();
}
return ImmutableSet.of(key - 2, key - 1);
}
@Override
public Integer load(Integer key)
throws Exception {
if (key <= 1) {
return 1;
}
return cache.get(key - 2) + cache.get(key - 1);
}
}
}
我已经对它进行了单元测试,看起来运行正确。
load
方法中语句的仔细排序,避免了递归加载。我不确定是否可以将其扩展到超出“玩具”示例的范围。 - Patrick MfibonacciCache.getUnchecked(N)
就意味着你会得到一个深度为N
的递归,这将导致堆栈溢出。除此之外,他们通过迭代i
来作弊。如果他们只是调用了getUnchecked(10)
会发生什么? - maaartinus