Java 8是否缓存了对供应商的支持?
番石榴图书馆拥有自己的Supplier
,不会扩展Java 8 Supplier
。 番石榴也为供应商提供了一个缓存 – Suppliers#memoize
。
是否有类似的东西,但对于Java 8供应商?
最简单的解决方案是
public static Supplier memoize(Supplier original) { ConcurrentHashMap
但是,最简单的并不总是最有效的。
如果你想要一个干净有效的解决方案,诉诸匿名内部类来保持可变状态将获得回报:
public static Supplier memoize1(Supplier original) { return new Supplier () { Supplier 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
标志,因为在初始化期间并发访问的情况下,多个线程可能在替换委托之前等待方法的条目。 因此,这些线程需要检测已经完成初始化。 所有后续访问都将读取新的委托供应商并快速获取值。
没有内置的Java函数用于memoization,虽然它实现起来并不是很难,例如,像这样:
public static Supplier memoize(Supplier delegate) { AtomicReference value = new AtomicReference<>(); return () -> { T val = value.get(); if (val == null) { val = value.updateAndGet(cur -> cur == null ? Objects.requireNonNull(delegate.get()) : cur); } return val; }; }
注意,存在不同的实现方法。 如果被记忆的供应商从不同的线程同时多次请求,则上述实现可以多次调用该代表。 有时这种实现比使用lock的显式同步更受欢迎。 如果首选锁定,则可以使用DCL:
public static Supplier memoizeLock(Supplier delegate) { AtomicReference 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; }; }
另请注意,正如@LouisWasserman在评论中正确提到的那样,您可以使用方法参考轻松地将JDK供应商转换为Guava供应商,反之亦然:
java.util.function.Supplier jdkSupplier = () -> "test"; com.google.common.base.Supplier guavaSupplier = jdkSupplier::get; java.util.function.Supplier jdkSupplierBack = guavaSupplier::get;
因此,在Guava和JDK函数之间切换并不是一个大问题。