Java8是否对供应商提供缓存支持?

Java8是否对供应商提供缓存支持?,java,java-8,guava,Java,Java 8,Guava,guava库有自己的库,它不扩展Java8。此外,番石榴还为供应商提供了缓存 除了Java 8供应商之外,还有类似的功能吗?没有用于记忆的内置Java函数,尽管实现它并不困难,例如: public static <T> Supplier<T> memoize(Supplier<T> delegate) { AtomicReference<T> value = new AtomicReference<>(); return

guava库有自己的库,它不扩展Java8。此外,番石榴还为供应商提供了缓存


除了Java 8供应商之外,还有类似的功能吗?

没有用于记忆的内置Java函数,尽管实现它并不困难,例如:

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;
    };
}
公共静态供应商备忘录(供应商代表){
AtomicReference值=新的AtomicReference();
返回()->{
T val=value.get();
if(val==null){
val=value.updateAndGet(cur->cur==null?
Objects.requirennull(delegate.get()):cur);
}
返回val;
};
}
请注意,存在不同的实现方法。如果记忆供应商同时多次从不同线程请求,则上述实现可能多次调用委托。有时,这种实现优于使用锁的显式同步。如果首选锁定,则可以使用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;
    };
}
公共静态供应商备忘录(供应商代表){
AtomicReference值=新的AtomicReference();
返回()->{
T val=value.get();
if(val==null){
已同步(值){
val=value.get();
if(val==null){
val=Objects.requirennull(delegate.get());
值集(val);
}
}
}
返回val;
};
}
另外请注意,正如@LouisWasserman在评论中正确提到的,您可以使用方法引用轻松地将JDK供应商转换为Guava供应商,反之亦然:

java.util.function.Supplier<String> jdkSupplier = () -> "test";
com.google.common.base.Supplier<String> guavaSupplier = jdkSupplier::get;
java.util.function.Supplier<String> jdkSupplierBack = guavaSupplier::get;
java.util.function.Supplier jdkSupplier=()->“测试”;
com.google.common.base.Supplier guavaSupplier=jdkSupplier::get;
java.util.function.Supplier jdkSupplierBack=guavaSupplier::get;

因此,在Guava和JDK函数之间切换不是什么大问题。

最简单的解决方案是

public static <T> Supplier<T> memoize(Supplier<T> original) {
    ConcurrentHashMap<Object, T> store=new ConcurrentHashMap<>();
    return ()->store.computeIfAbsent("dummy", key->original.get());
}
公共静态供应商备忘录(供应商原件){
ConcurrentHashMap存储=新建ConcurrentHashMap();
return()->store.computeFabSent(“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();
        }
    };
}
公共静态供应商备忘录1(供应商原件){
退回新供应商(){
供应商代表=此::首次;
布尔初始化;
公共部门得不到{
返回delegate.get();
}
private synchronized T firstTime(){
如果(!已初始化){
T value=original.get();
委托=()->值;
初始化=真;
}
返回delegate.get();
}
};
}
这将使用一个代理供应商,该供应商将执行第一次操作,然后将自己替换为一个无条件返回第一次评估捕获结果的供应商。由于它具有
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;
}

静态java.util.function.Supplier memoize(java.util.function.Supplier不完全正确,但您只需在末尾编写
::get
即可轻松在j.u.f.Suppliers和c.g.c.b.Suppliers之间转换。正如@LouisWasserman所建议的,您可以通过基本上执行“return Suppliers.memoize”来为番石榴Suppliers制作包装器::memoize(委托::获取)::获取很遗憾Suppliers.memoize没有进入jdk8标准库,因为它对我来说似乎是一个非常低风险的添加。在这种情况下,你真的不需要
AtomicReference
,是吗?它似乎被用作lambda可以关闭的可变容器。如果你想保存一个对象我想你可以返回一个带有volatile
字段的匿名类实例。在
this
@Lii,
原子引用
上的同步只是一个带有单个字段的对象,它提供了此处所需的volatile读/写语义。可以用匿名类volatile字段替换它(仅在第二个示例中,而不是在第一个示例中),但这类优化是否重要并不十分明显。此外,锁定公共可用对象被认为是一种不好的做法。您可以通过记住表单的另一个供应商
()->val
。这样,您就可以使用捕获值的
final
字段语义。@glts:
委托.get()
将在第一次调用的
synchronized
方法
firstTime()
或与
()->value
lambda表达式,而
value
实际上是final。访问该捕获的值相当于读取一个
final
字段,该字段无需额外同步,是安全的。如果线程看到
委托
引用的过时值,它将通过
已同步
firstTime()
方法进行一次调用,并在调用后知道最新的值,因此所有后续调用都会快速进行。在这种情况下,
委托
为什么不需要标记为
易失性
?@Mark Elliot:捕获的局部变量值具有
最终
字段seman