转载

利用Lambda表达式进行Java中的惰性求值

在java中,懒性求值也称懒计算lazy evaluation功能可能被忽视了(实际上,在语言层面,它几乎仅限于最小求值的实现) - 例如Scala等高级语言区分了按值调用和按名称调用,或引入像lazy这样的专用关键字。

虽然Java 8通过提供Lazy Sequence概念(我们都知道它为java.util.stream.Stream而生)实现了改进 ,但今天我们将跳过这一点,关注引入Lambda表达式是如何给我们带来一种新的轻量级方法。

在scala中实现Lambda支持的惰求值

每当我们想惰性计算Scala中的方法参数时,我们就可以采用“按名称调用”方式。

让我们创建一个简单的foo方法,接受一个String实例并返回String:

def foo(b: String): String = b

现在,如果我们想让b懒计算,我们可以利用call-by-name语法并简单地在b的类型声明中添加两个符号:

def foo(b:=> String):String = b

如果我们试图javap的反向工程生成的* .class文件,我们会看到:


Compiled from "LazyFoo.scala"
public final class LazyFoo {
public static java.lang.String foo(scala.Function0<java.lang.String>);
Code:
0: getstatic #17
// Field LazyFoo$.MODULE$:LLazyFoo$;
3: aload_0
4: invokevirtual #19
// Method LazyFoo$.foo:(Lscala/Function0;)Ljava/lang/String;
7: areturn
}

传递给我们方法的参数不再是String,而是一个  Function0 <String> - 这使得可以使用惰性求值表达式 - 只要我们不调用它,就不会触发计算- 就是如此容易。

在Java中

当我们需要懒计算。返回一个T时,我们只要按照上面思路,将计算用等同于Function0的 Supplier<T> 实例包装:


Integer v1 = 42; // eager

Supplier<Integer> v2 = () -> 42;
// lazy

如果我们需要从数据库获得结果,那将更加实用:


Integer v1 = compute(); //eager

Supplier<Integer> value = () -> compute();
// lazy

这样,只有value方法被调用时,才会从数据库查询计算。

惰性求值也可以作为输入参数,只有方法体内使用这个函数才会计算。


private static int computeLazily(Supplier<Integer> value) {
// ...
}

如果仔细观察Java 8中引入的API,您

会注意到这种模式经常被使用,如Optional#orElseGet 是等同于 Optional#orElse惰性求值方法,如果没我们介绍的模式实现, Optional会毫无作用。

线程安全

遗憾的是,这种简单的方法存在缺陷 - 每次函数调用都会触发计算 - 不仅多线程环境是这样,同一线程连续调用也是这样的情况 ,当然 只要我们知道这个特点就可以了,合理应用这个技术就好。

有记忆的惰性求值

如前所述,基于lambda的方法在某些情况下可能是有缺陷的,因为求值的结果永远不会被记忆化。

为了解决这个问题,我们需要构建一个专用工具,比方说Lazy工具类:


@RequiredArgsConstructor
public class NaiveLazy<T> implements Supplier<T> {
private final Supplier<T> supplier;
private T value;

@Override
public T get() {
if (value == null) {
value = supplier.get();
}
return value;
}
}

上面并不是线程安全的。

幸运的是,使其成为线程安全只需要确保多个线程在尝试获取值时不会触发相同的计算 - 这可以通过使用双重检查锁定模式轻松实现(我们可以简单地在get()方法上同步,但这会引入不必要的争用):


@RequiredArgsConstructor
public class Lazy<T> implements Supplier<T> {
private final Supplier<T> supplier;
private volatile T value;

@Override
public T get() {
if (value == null) {
synchronized (this) {
if (value == null) {
value = supplier.get();
}
}
}
return value;
}
}

现在我们在Java中实现了惰求值的全功能实现。由于它没有在语言级别实现,因此需要支付与创建新对象相关的额外成本。

banq评: 可惜了,不是无锁记忆性懒计算,在jdon框架中我N多年前使用LAMX实现了无锁懒计算。

原文:

https://4comprehension.com/leveraging-lambda-expressions-for-lazy-evaluation-in-java/

[该贴被banq于2018-08-28 20:19修改过]

[该贴被banq于2018-08-28 20:20修改过]

原文  https://www.jdon.com/49906
正文到此结束
Loading...