質問を書き直したので、ここに新しい回答があります。
まず、それを呼び出すので、あなたの実装computeNonRecursivelly
はまだ再帰的であるように見えます。getOrEnqueue
計算には2つのステップが必要なため、直接使用できないと思いますCache
.1つは必要な値の依存関係を示し、もう1つは依存関係が満たされたときに計算を実行します。ただし、循環依存関係がない場合にのみ機能します(再帰と同じ要件です)。
そうすれば、まだキャッシュにない依存関係 (およびその依存関係など) をキューに入れ、正しい順序でそれらを計算できます。次の行に沿ったもの:
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) && // Deduplication in the dependencies
cache.getIfPresent(dependency) == null) {
// We need to compute it.
toCompute.push(dependency);
// We also need its dependencies.
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());
// The last computed value is for our key.
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);
}
}
}
単体テストを実行しましたが、正しく実行されているようです。