List Java 8-累积并创建新集合

List Java 8-累积并创建新集合,list,collections,java-8,accumulator,collectors,List,Collections,Java 8,Accumulator,Collectors,使用Java8,我想从列表中创建一个新集合,并在此过程中累积一个总和 “源”列表由如下所示的对象组成: class Event { String description; double sum; } { { "desc1", 10.0 }, {"desc2", 14.0 }, {"desc3", 5.0 } } List<SummedEvent> summedEvents = events.stream().collect(EventConsumer::new, Even

使用Java8,我想从列表中创建一个新集合,并在此过程中累积一个总和

“源”列表由如下所示的对象组成:

class Event {
  String description;
  double sum;
}
{ { "desc1", 10.0 }, {"desc2", 14.0 }, {"desc3", 5.0 } }
List<SummedEvent> summedEvents = events.stream().collect(EventConsumer::new, EventConsumer::accept, EventConsumer::combine).summedEvents();
summedEvents.forEach((se) -> System.out.println(String.format("%s, %2f, %2f", se.description, se.sum, se.runningTotal)));
class EventConsumer {
    private List<SummedEvent> summedEvents = new ArrayList<>();
    private double runningTotal = 0;

    public void accept(Event event) {
        runningTotal += event.sum;
        summedEvents.add(new SummedEvent(event.description, event.sum, runningTotal));
    }
    public void combine(EventConsumer other) {
        this.summedEvents.addAll(other.summedEvents);
        this.runningTotal += other.runningTotal;
    }

    public List<SummedEvent> summedEvents() {
        return summedEvents;
    }
}
double[] acc = originalList.stream().mapToDouble(e -> e.sum).toArray();
Arrays.parallelPrefix(acc, Double::sum);

List<CustomEvent> lx = IntStream.range(0, originalList.size())
                                .parallel()
                                .mapToObj(i -> new CustomEvent(originalList.get(i), acc[i]))
                                .collect(toList());
使用如下示例列表:

class Event {
  String description;
  double sum;
}
{ { "desc1", 10.0 }, {"desc2", 14.0 }, {"desc3", 5.0 } }
List<SummedEvent> summedEvents = events.stream().collect(EventConsumer::new, EventConsumer::accept, EventConsumer::combine).summedEvents();
summedEvents.forEach((se) -> System.out.println(String.format("%s, %2f, %2f", se.description, se.sum, se.runningTotal)));
class EventConsumer {
    private List<SummedEvent> summedEvents = new ArrayList<>();
    private double runningTotal = 0;

    public void accept(Event event) {
        runningTotal += event.sum;
        summedEvents.add(new SummedEvent(event.description, event.sum, runningTotal));
    }
    public void combine(EventConsumer other) {
        this.summedEvents.addAll(other.summedEvents);
        this.runningTotal += other.runningTotal;
    }

    public List<SummedEvent> summedEvents() {
        return summedEvents;
    }
}
double[] acc = originalList.stream().mapToDouble(e -> e.sum).toArray();
Arrays.parallelPrefix(acc, Double::sum);

List<CustomEvent> lx = IntStream.range(0, originalList.size())
                                .parallel()
                                .mapToObj(i -> new CustomEvent(originalList.get(i), acc[i]))
                                .collect(toList());
结果列表应该如下所示

  • 描述1,10.0,10.0
  • 描述2,14.0,24.0
  • 描述3,5.0,29.0
我知道如何求和以得到最终的和,在本例中是29.0,但我想创建结果列表,同时在过程中累积和


如何使用Java8实现这一点?

您可以通过实现自己的收集器来实现这一点,从而一起执行映射和求和。您的流式处理代码如下所示:

class Event {
  String description;
  double sum;
}
{ { "desc1", 10.0 }, {"desc2", 14.0 }, {"desc3", 5.0 } }
List<SummedEvent> summedEvents = events.stream().collect(EventConsumer::new, EventConsumer::accept, EventConsumer::combine).summedEvents();
summedEvents.forEach((se) -> System.out.println(String.format("%s, %2f, %2f", se.description, se.sum, se.runningTotal)));
class EventConsumer {
    private List<SummedEvent> summedEvents = new ArrayList<>();
    private double runningTotal = 0;

    public void accept(Event event) {
        runningTotal += event.sum;
        summedEvents.add(new SummedEvent(event.description, event.sum, runningTotal));
    }
    public void combine(EventConsumer other) {
        this.summedEvents.addAll(other.summedEvents);
        this.runningTotal += other.runningTotal;
    }

    public List<SummedEvent> summedEvents() {
        return summedEvents;
    }
}
double[] acc = originalList.stream().mapToDouble(e -> e.sum).toArray();
Arrays.parallelPrefix(acc, Double::sum);

List<CustomEvent> lx = IntStream.range(0, originalList.size())
                                .parallel()
                                .mapToObj(i -> new CustomEvent(originalList.get(i), acc[i]))
                                .collect(toList());

如果您将按顺序运行管道,则可以将此小技巧与
peek
结合使用

double[] acc = {0};
List<CustomEvent> list = originalList.stream()
                                     .peek(e -> acc[0] += e.sum)
                                     .map(e -> new CustomEvent(e, acc[0]))
                                     .collect(toList());

parallelPrefix
将应用您要查找的总和的减少。然后,您只需将索引流化,并将每个事件映射到其相应的累计和。

您如何只生成结果列表中的一个元素?。。。考虑到这一点,我不确定它是否可以在一个过程中并行完成。我根本不知道如何并行完成,因为它需要排序,我想OP已经意识到了这一点。Well
parallelPrefix
并行地进行“有序”累积。因此,我假设它可以通过一些额外的代码来完成。我看了parallelPrefix—“一些”code=“相当多”的代码,其中大部分我不理解:)你本质上是在寻找前缀操作(如果你的操作符是关联的,它会并行工作。)流不支持前缀操作,但有一个用于数组的前缀操作。不要这样编写代码(“this”表示编写代码,如果有人将流并行化,代码将自动中断)。这是2015版的数据竞赛编码,因为“我永远不会使用线程。”@BrianGoetz你是说
peek
版?是的,反正我也不喜欢用它,因为它经常有副作用;也许我的回答不够清楚,我编辑了它。@svaret
parallelPrefix
已经并行运行了(顾名思义)。不,您不会有任何问题。@svaret请注意,并行运行并不意味着它总是比顺序计算快。对于小型阵列,创建所有这些线程的开销可能会很昂贵。@svaret是的,我想这需要经验。如果您使用的是我的第一个“解决方案”,它不是一种功能性方法,因为
peek
方法有副作用。函数式编程语言有一条经验法则,即函数永远不会有副作用。