如何用Java Stream处理有序ArrayList,生成带差值的RecordB
需求实现:用Java Stream将RecordA列表转换为RecordB列表
给定的Java类定义
import java.util.*; class RecordA{ Integer id; Integer value; RecordA(Integer id, Integer value) { this.id = id; this.value = value; } Integer getId() { return id;} Integer getValue() { return value;} } class RecordB { Integer id; Integer value; Integer diff; RecordB(Integer id, Integer value, Integer diff) { this.id = id; this.value = value; this.diff = diff; } Integer getId() { return id;} Integer getValue() { return value;} Integer getDiff() { return diff;} } class HelloWorld { public static void main(String[] args) { List<RecordA> listA = new ArrayList<>(); RecordA recordA1 = new RecordA(1,10); listA.add(recordA1); RecordA recordA2 = new RecordA(2,15); listA.add(recordA2); RecordA recordA3 = new RecordA(3,25); listA.add(recordA3); RecordA recordA4 = new RecordA(4,30); listA.add(recordA4); System.out.println(listA.size()); } }
需求说明
现有一个存储有序RecordA实例的ArrayList,RecordA包含id和value属性。需要遍历该列表,对比当前元素与前一个元素的value值,计算两者的差值(当前value - 前一个value,第一条记录差值为0),并将结果映射为RecordB实例(包含相同的id、value以及计算得到的diff)。最终得到的RecordB列表应如下:
- 1, 10, 0 //(10-0)
- 2, 15, 5 //(15-10)
- 3, 25, 10 //25-15
- 4, 30, 5 //30-25
要求避免使用普通for循环和prev_val变量,通过Java Stream实现该需求。
解决方案
提供两种符合要求的实现方式:
方式一:利用IntStream遍历索引
通过IntStream.range生成列表的索引范围,根据索引获取当前元素和前一个元素(第一条元素特殊处理),直接映射为RecordB:
import java.util.stream.Collectors; import java.util.stream.IntStream; // ... 其他类定义不变 List<RecordB> listB = IntStream.range(0, listA.size()) .mapToObj(index -> { RecordA current = listA.get(index); int prevValue = index == 0 ? 0 : listA.get(index - 1).getValue(); int diff = current.getValue() - prevValue; return new RecordB(current.getId(), current.getValue(), diff); }) .collect(Collectors.toList());
方式二:利用AtomicReference维护前值状态(仅适用于串行流)
如果不想依赖索引,可以使用AtomicReference保存前一个元素的value,在流处理中更新状态:
import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; // ... 其他类定义不变 AtomicReference<Integer> prevValueRef = new AtomicReference<>(0); List<RecordB> listB = listA.stream() .map(recordA -> { int currentValue = recordA.getValue(); int diff = currentValue - prevValueRef.get(); RecordB recordB = new RecordB(recordA.getId(), currentValue, diff); prevValueRef.set(currentValue); return recordB; }) .collect(Collectors.toList());
注意:这种方式仅支持串行流,并行流会因竞态条件导致状态混乱,而原列表是有序的,串行流完全满足需求。
验证代码
将上述任意一种实现替换HelloWorld类的main方法即可验证结果,示例如下:
public static void main(String[] args) { List<RecordA> listA = new ArrayList<>(); listA.add(new RecordA(1,10)); listA.add(new RecordA(2,15)); listA.add(new RecordA(3,25)); listA.add(new RecordA(4,30)); // 使用方式一的实现 List<RecordB> listB = IntStream.range(0, listA.size()) .mapToObj(index -> { RecordA current = listA.get(index); int prevValue = index == 0 ? 0 : listA.get(index - 1).getValue(); int diff = current.getValue() - prevValue; return new RecordB(current.getId(), current.getValue(), diff); }) .collect(Collectors.toList()); // 输出结果 listB.forEach(b -> System.out.printf("%d, %d, %d%n", b.getId(), b.getValue(), b.getDiff())); }
执行后会输出:
1, 10, 0 2, 15, 5 3, 25, 10 4, 30, 5
内容的提问来源于stack exchange,提问作者Grafana Next
相关产品推荐
相关产品推荐

