在使用java8的lamda表达式中,外部变量必须使用final修饰。但是在某些场景中,我们需要使用外部变量并且还要修改外部变量。
以求均分权重加权移动平均线为例。
public static List<Float> average() {
// 原始数据
List<Float> list = Arrays.asList(1f, 2f, 3f, 4f, 5f);
final Float before = null;
return list.stream().map(item -> {
if (Objects.isNull(before) {
// 第一个等于自身
before = item;
return item;
} else {
float result = (before + item) / 2; // 结果为前一个值加上当前值除以2
before = item; // final 修饰的变量无法赋值
return result;
}
}).toList();
}
因为final修饰的变量无法赋值,我们可以定义一个包装类,这样final修饰的变量不变,我们通过修改包装类内部的值达到修改的目的。
使用自定义包装类
通过定义一个包装类,通过修改包装类的值,而不修改包装类的引用达到目的。
@Data
public static class Wrapper<T> {
private T value;
}
public static List<Float> average2() {
List<Float> list = Arrays.asList(1f, 2f, 3f, 4f, 5f);
final Wrapper<Float> before = new Wrapper();
return list.stream().map(item -> {
if (Objects.isNull(before.getValue())) {
// 第一个等于自身
before.setValue(item);
return item;
} else {
float result = (before.getValue() + item) / 2; // 结果为前一个值加上当前值除以2
before.setValue(item);
return result;
}
}).toList();
}
使用AtomicReference
AtomicReference是java自带的,使用AtomicReference类,就不用另外自定义一个类了。虽然AtomicReference可以解决这个场景的问题,但是AtomicReference这个类的本意是用来实现CAS自旋锁的,并不是为了解决这个场景的问题。
public static List<Float> average3() {
List<Float> list = Arrays.asList(1f, 2f, 3f, 4f, 5f);
final AtomicReference<Float> before = new AtomicReference();
return list.stream().map(item -> {
if (Objects.isNull(before.get())) {
// 第一个等于自身
before.set(item);
return item;
} else {
float result = (before.get() + item) / 2; // 结果为前一个值加上当前值除以2
before.set(item);
return result;
}
}).toList();
}
综上,在需要修改final变量的情况中,可以使用包装类,其中一个现成的包装类就是AtomicReference,我们可以使用该类来解决这类问题。