Ответ 1
Простейшим решением было бы
public static <T> Supplier<T> memoize(Supplier<T> original) {
ConcurrentHashMap<Object, T> store=new ConcurrentHashMap<>();
return ()->store.computeIfAbsent("dummy", key->original.get());
}
Однако самый простой не всегда самый эффективный.
Если вы хотите получить чистое и эффективное решение, прибегая к анонимному внутреннему классу для хранения изменчивого состояния, он окупится:
public static <T> Supplier<T> memoize1(Supplier<T> original) {
return new Supplier<T>() {
Supplier<T> delegate = this::firstTime;
boolean initialized;
public T get() {
return delegate.get();
}
private synchronized T firstTime() {
if(!initialized) {
T value=original.get();
delegate=() -> value;
initialized=true;
}
return delegate.get();
}
};
}
В этом случае используется поставщик делегатов, который будет выполнять операцию в первый раз и впоследствии, заменит себя поставщиком, который безоговорочно вернет захваченный результат первой оценки. Поскольку он имеет семантику полей final
, он может безоговорочно возвращаться без какой-либо дополнительной синхронизации.
Внутри метода synchronized
firstTime()
по-прежнему существует флаг initialized
, потому что в случае одновременного доступа во время инициализации несколько потоков могут ждать в элементе методов до того, как делегат будет заменен. Следовательно, эти потоки должны обнаружить, что инициализация уже выполнена. Все последующие обращения будут читать новый поставщик делегатов и быстро получать значение.