Improved the memoizer by adding fast path and slightly simplified existing logic, updated copyright year in readme document.

pull/134/head
Project Nayuki 3 years ago
parent 04dd0fc06c
commit cb4643510c

@ -60,7 +60,7 @@ Examples
License License
------- -------
Copyright © 2019 Project Nayuki. (MIT License) Copyright © 2021 Project Nayuki. (MIT License)
[https://www.nayuki.io/page/fast-qr-code-generator-library](https://www.nayuki.io/page/fast-qr-code-generator-library) [https://www.nayuki.io/page/fast-qr-code-generator-library](https://www.nayuki.io/page/fast-qr-code-generator-library)
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of

@ -24,10 +24,10 @@
package io.nayuki.fastqrcodegen; package io.nayuki.fastqrcodegen;
import java.lang.ref.SoftReference; import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function; import java.util.function.Function;
@ -35,7 +35,7 @@ import java.util.function.Function;
final class Memoizer<T,R> { final class Memoizer<T,R> {
private final Function<T,R> function; private final Function<T,R> function;
private Map<T,SoftReference<R>> cache = new HashMap<>(); Map<T,SoftReference<R>> cache = new ConcurrentHashMap<>();
private Set<T> pending = new HashSet<>(); private Set<T> pending = new HashSet<>();
@ -47,21 +47,30 @@ final class Memoizer<T,R> {
// Computes function.apply(arg) or returns a cached copy of a previous call. // Computes function.apply(arg) or returns a cached copy of a previous call.
public R get(T arg) { public R get(T arg) {
// Non-blocking fast path
{
SoftReference<R> ref = cache.get(arg);
if (ref != null) {
R result = ref.get();
if (result != null)
return result;
}
}
// Sequential slow path
while (true) { while (true) {
synchronized(this) { synchronized(this) {
if (cache.containsKey(arg)) { SoftReference<R> ref = cache.get(arg);
SoftReference<R> ref = cache.get(arg); if (ref != null) {
R result = ref.get(); R result = ref.get();
if (result != null) if (result != null)
return result; return result;
cache.remove(arg); cache.remove(arg);
} }
// Now cache.containsKey(arg) == false assert !cache.containsKey(arg);
if (!pending.contains(arg)) { if (pending.add(arg))
pending.add(arg);
break; break;
}
try { try {
this.wait(); this.wait();
@ -73,9 +82,7 @@ final class Memoizer<T,R> {
try { try {
R result = function.apply(arg); R result = function.apply(arg);
synchronized(this) { cache.put(arg, new SoftReference<>(result));
cache.put(arg, new SoftReference<>(result));
}
return result; return result;
} finally { } finally {
synchronized(this) { synchronized(this) {

Loading…
Cancel
Save