Che*_*rry 27 java guava java-8
guava库有它自己的Supplier,不扩展Java 8 Supplier.番石榴还为供应商提供缓存 - Suppliers#memoize.
是否有类似的东西,但对于Java 8供应商?
Tag*_*eev 25
没有内置的Java函数用于memoization,虽然它实现起来并不是很难,例如,像这样:
public static <T> Supplier<T> memoize(Supplier<T> delegate) {
AtomicReference<T> value = new AtomicReference<>();
return () -> {
T val = value.get();
if (val == null) {
val = value.updateAndGet(cur -> cur == null ?
Objects.requireNonNull(delegate.get()) : cur);
}
return val;
};
}
Run Code Online (Sandbox Code Playgroud)
注意,存在不同的实现方法.如果被记忆的供应商从不同的线程同时多次请求,则上述实现可以多次调用该代表.有时这种实现比使用lock的显式同步更受欢迎.如果首选锁定,则可以使用DCL:
public static <T> Supplier<T> memoizeLock(Supplier<T> delegate) {
AtomicReference<T> value = new AtomicReference<>();
return () -> {
T val = value.get();
if (val == null) {
synchronized(value) {
val = value.get();
if (val == null) {
val = Objects.requireNonNull(delegate.get());
value.set(val);
}
}
}
return val;
};
}
Run Code Online (Sandbox Code Playgroud)
另请注意,正如@LouisWasserman在评论中正确提到的那样,您可以使用方法参考轻松地将JDK供应商转换为番石榴供应商,反之亦然:
java.util.function.Supplier<String> jdkSupplier = () -> "test";
com.google.common.base.Supplier<String> guavaSupplier = jdkSupplier::get;
java.util.function.Supplier<String> jdkSupplierBack = guavaSupplier::get;
Run Code Online (Sandbox Code Playgroud)
因此,在Guava和JDK函数之间切换并不是一个大问题.
Hol*_*ger 17
最简单的解决方案是
public static <T> Supplier<T> memoize(Supplier<T> original) {
ConcurrentHashMap<Object, T> store=new ConcurrentHashMap<>();
return ()->store.computeIfAbsent("dummy", key->original.get());
}
Run Code Online (Sandbox Code Playgroud)
然而,最简单的并不总是最有效的.
如果你想要一个干净有效的解决方案,诉诸匿名内部类来保持可变状态将会得到回报:
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();
}
};
}
Run Code Online (Sandbox Code Playgroud)
这将使用委托供应商,该委托供应商将在第一次操作时执行,之后将其自身替换为无条件返回第一次评估的捕获结果的供应商.由于它具有final字段语义,因此无需任何额外同步即可无条件地返回.
在synchronized方法内部firstTime(),仍然initialized需要一个标志,因为在初始化期间并发访问的情况下,多个线程可以在替换委托之前等待方法的条目.因此,这些线程需要检测已经完成了初始化.所有后续访问都将读取新的委托供应商并快速获取值.
Java 8 上 Guava 20 的简单包装器:
static <T> java.util.function.Supplier<T> memoize(java.util.function.Supplier<? extends T> supplier) {
return com.google.common.base.Suppliers.memoize(supplier::get)::get;
}
Run Code Online (Sandbox Code Playgroud)