You can create a custom class, and use a mutable reduction:
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(2, 4, 3);
Result result = numbers.stream().collect(Result::new, Result::consume, Result::combine);
System.out.println("result = " + result);
}
private static class Result {
private int sum = 0;
private int product = 1;
public void consume(int i) {
sum += i + 3;
product *= i;
}
public void combine(Result r) {
// READ note below
sum += r.sum + 3;
product *= r.product;
}
@Override
public String toString() {
return "Result{" +
"sum=" + sum +
", product=" + product +
'}';
}
}
But I doubt you will gain much by doing that over simply iterating twice as you're doing.
EDIT:
Note that the combine() method above, invoked when using a parallel stream, will produce results that are inconsistent with a sequential stream. The problem is caused, as Holger mentions in the comments, by the fact that the operation doesn't respect the identity preconditions of a reduction: applying the reduction on any value V with the identity is supposed to produce V, but it produces V + 3 with 0 as identity.
Make sure not to use a parallel stream to compute that result.