A running total carries state between elements, which a stream deliberately does not. A one-element array is the usual escape hatch - and it is only correct because this pipeline is sequential and ordered.
record Txn(int at, double delta) {}
var deposits = List.of(new Txn(1, 500.0), new Txn(5, 200.0));
var withdrawals = List.of(new Txn(3, -120.0), new Txn(7, -80.0));
double[] balance = {0.0}; // sequential + ordered, or this is a data race
Stream.concat(deposits.stream(), withdrawals.stream())
.sorted(Comparator.comparingInt(Txn::at))
.forEach(t -> {
balance[0] += t.delta();
System.out.println(t.at() + "s " + t.delta() + " -> balance " + balance[0]);
});
1s 500.0 -> balance 500.0
3s -120.0 -> balance 380.0
5s 200.0 -> balance 580.0
7s -80.0 -> balance 500.0
Run this yourself in the Online Java Compiler, spin up a live REST API in the API Sandbox, or practise with Java interview questions.
Published 2026-09-20