今天使用流对一个需求列表进行分组,分组后得到一个map集合
我需要循环着一个map集合,同时需要在循环外初始化一个变量,在循环内累加;
代码如下:
Integer count = 0;
//安全库存计算模式
demandGroup.forEach((key,demands) ->{
if (count == 0){
count = count + 1;
return;
}
//生成供应
createSupply(planContainer,plan,demands);
});
但是着端代码是存在问题的,直接就编译报错了;
因为Lambda表达式中引用的局部变量必须是final或者effectively final的,
这里count在Lambda表达式中被赋值,这使得它不再是final或effectively final的,因此编译器会报错;
BigDecimal supplyCount = BigDecimal.ZERO;
//安全库存计算模式
demandGroup.forEach((key,demands) ->{
if (BigDecimal.ZERO.compareTo(supplyCount) == 0){
supplyCount.add(new BigDecimal(1));
return;
}
//生成供应
createSupply(planContainer,plan,demands);
});
这样编译不再报错,但是运行结果,supplyCount始终 为0,createSupply(planContainer,plan,demands);这一句代码始终没有执行;
那是因为,对象名称是相同的,但是在Lambda表达式中supplyCount已经是另外一个对象了,所以我每次add的时候都是给新的对象 +1,循环外的supplyCount一直都是0,最终导致createSupply(planContainer,plan,demands);这一句代码始终都没有执行;
可以使用一个一元素数组或者一个包装类(如AtomicInteger或AtomicReference)
代码如下:
AtomicReference<BigDecimal> supplyCount = new AtomicReference<>(BigDecimal.ZERO);
//安全库存计算模式
demandGroup.forEach((key,demands) ->{
if (BigDecimal.ZERO.compareTo(supplyCount.get()) == 0){
supplyCount.set(supplyCount.get().add(BigDecimal.ONE));
return;
}
//生成供应
createSupply(planContainer,plan,demands);
});
AtomicReference是Java提供的一个线程安全的引用类型变量,它提供了一些方法来进行原子操作。虽然AtomicReference本身并没有提供累加操作,但是我们可以通过get和set方法来实现累加操作。
在Java中,Lambda表达式中引用的局部变量必须是final或者effectively final的。这意味着这些变量在初始化后就不能再被修改。然而,这个规则只适用于变量本身,而不适用于变量引用的对象。如果变量引用的是一个对象,那么这个对象的状态是可以在Lambda表达式中被改变的。
如果你有一个数组或者一个包装类(如AtomicInteger
或AtomicReference
),你可以在Lambda表达式中修改这个数组或包装类的状态,即使这个数组或包装类的引用是final的。这是因为你实际上并没有改变数组或包装类的引用,而只是改变了它们的状态。
以下是一个示例,展示了如何在Lambda表达式中修改一个数组的元素:
final int[] count = new int[1];
Arrays.asList(1, 2, 3, 4, 5).forEach(i -> count[0] += i);
System.out.println(count[0]); // 输出15
在这个示例中,我们首先创建了一个包含一个元素的数组count
,然后在Lambda表达式中修改了这个数组的第一个元素。尽管count
的引用是final的,但我们仍然可以修改它的状态。
虽然平时很喜欢使用流处理业务,但是出现这种bug,归根结底还是我对Lambda不够熟悉,后续深入研究一下;