If you have such a dependency between the termination criteria and the Collector
’s result, using mutable state is unavoidable. But as long as you don’t need parallel execution, the implementation can be straight-forward:
class MutableDouble {
double sum;
}
MutableDouble sumHolder=new MutableDouble();
DoubleStream ds=IntStream.iterate(1, (i -> i + 1))
.mapToDouble(n -> 1 / ((double)n * n));
ds.peek(term -> sumHolder.sum+=term)
.filter(term -> Math.abs(term)<1e-12*Math.abs(sumHolder.sum))
.findFirst();
System.out.println(sumHolder.sum);
Since it will sum up before comparing it is not exactly the same as your original logic but rather like:
double sum = 0;
for (double n = 1; ; n++) {
double term = 1 / (n * n);
sum += term;
if (Math.abs(term) <= 1e-12 * Math.abs(sum)) {
break;
}
}
If you insist on the original logic it has to be slightly more complicated:
class MutableDouble {
double sum, next;
void add(double d) {
sum=next;
next+=d;
}
}
MutableDouble sumHolder=new MutableDouble();
DoubleStream ds=IntStream.iterate(1, (i -> i + 1))
.mapToDouble(n -> 1 / ((double)n * n));
ds.peek(sumHolder::add)
.filter(term -> Math.abs(term)<1e-12*Math.abs(sumHolder.sum))
.findFirst();