Самым простым решением было бы
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
флаг необходимо потому, что в случае одновременного доступа во время инициализации, несколько потоков могут ждать на входе методы перед делегатом был заменен. Следовательно, эти потоки должны обнаружить, что инициализация уже выполнена. Все последующие обращения будут читать новый поставщик делегатов и быстро получить значение.
Не совсем, но вы можете легко конвертировать между j.u.f.Suppliers и c.g.c.b.Suppliers, просто написав ':: get' в конце. –
как @LouisWasserman предлагает, вы можете сделать оболочку для guava. Поставщики :: memoize, в основном делая «return Suppliers.memoize (delegate :: get) :: get;» – jvdneste
Жаль, что Suppliers.memoize не попал в стандартную библиотеку jdk8, учитывая, что это похоже на очень низкий риск для меня. – jvdneste