Java 流式传输数组列表并与上一条记录进行比较

Java stream an array list and compare with previous record

提问人:Grafana Next 提问时间:11/17/2023 最后编辑:Grafana Next 更新时间:11/17/2023 访问量:62

问:

我有一个简单的 RecordA 类,它包含一个 id 和一个 int 值。多个“排序”的 RecordA 元素存储在一个列表中。

我想遍历列表并将当前元素与前一个元素进行比较,并找到它们值的差异。

法典:

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.value 与之前的 RecordA.value 进行比较,将结果映射到具有相同 id 和值的 RecordB,但存储当前上一个。

最后,RecordB 的 List 将包含

  • 1, 10, 0 //(10-0)
  • 2, 15, 5 //(15-10)
  • 3, 25, 10 //25-15
  • 4, 30, 5 //30-25

我想避免类for循环和previous_val变量。任何想法如何用流做到这一点?

Java 字典 java-8

评论

3赞 Louis Wasserman 11/17/2023
用流来做这件事比值得的要困难得多。使用循环。流不是为此而构建的。
1赞 Turing85 11/17/2023
扩展@LouisWasserman的评论:本质上是无国籍的。有些操作在某种程度上引入了约束,例如 .sorted()。如前所述,可以通过流实现这一点,但这是不值得的;代码将更难阅读和理解(并且可能更慢)。只需使用命令式方法即可。Stream
0赞 user207421 11/17/2023
我不同意。这很容易用 .@LouisWassermanCollector
0赞 Louis Wasserman 11/17/2023
@user207421:从技术上讲,可以使用 ,是的。然而,这比值得的要难得多。使用循环要好得多。Collector
1赞 Bohemian 11/17/2023
我会使用循环。更易于编码和阅读,并且确实要求您的流是非并行的。

答:

0赞 Reilas 11/17/2023 #1

"...我想避免类for循环和previous_val变量。有什么想法可以用流来做到这一点吗?

这是一种有点不直观的方法,实际上我不得不查一下。
StackOverflow – 使用 Foreach Lambda 中上一个元素的 Java 流

通常,的使用是聚合一组值,而不一定是比较和对比它们。
课程:聚合操作(Java™ 教程>集合)。

下面是使用 Collector 类和 Collector#of 方法的示例。

从本质上讲,在收集过程中,您可以从已收集的任何元素中检索上一个元素。

对于 BiConsumer 参数,a 是到目前为止收集的元素。

List<RecordB> l
    = listA.stream()
           .collect(
               Collector.<RecordA, List<RecordB>, List<RecordB>>of(
                   ArrayList::new,
                   (a, b) -> {
                       if (a.isEmpty()) a.add(new RecordB(b.id, b.value, 0));
                       else {
                           RecordB x = a.get(a.size() - 1);
                           a.add(new RecordB(b.id, b.value, b.value - x.value));
                       }
                   },
                   (a, b) -> {
                       a.addAll(b);
                       return a;
                   },
                   x -> x));

输出

1, 10, 0
2, 15, 5
3, 25, 10
4, 30, 5

最后,您可能希望摆脱 RecordB 类,而只使用 Map

Map<RecordA, Integer> m = new LinkedHashMap<>();
RecordA a, b;
m.put(a = listA.get(0), 0);
for (int i = 1, n = listA.size(); i < n; i++)
    m.put(b = listA.get(i), -a.value + (a = b).value);

评论

1赞 Holger 11/18/2023
此收集器不正确。当工作负载被拆分时,累加器函数会将每个块的第一个元素视为整个流的第一个元素,而不是将其与其前一个元素进行比较(这只能在合并函数中发生)。
3赞 Nick97 11/17/2023 #2

您可以使用 IntStream

IntStream.range(0, listA.size())
    .map(index -> 
        new RecordB(listA.get(index).getId(), listA.get(index).getValue(),  listA.get(index).getValue() - (index > 0 ? listA.get(index - 1).getValue() : 0))
    )
    .collect(Collectors.toList())