I am working with Java 8 streams, and would like to come up with a way to debug them. So i thought I could write a filter that printed out the elements at a stage of the stream, something like this:
int[] nums = {3, -4, 8, 4, -2, 17, 9, -10, 14, 6, -12};
int sum = Arrays.stream(nums)
.filter(w -> {System.out.print(" " + w); return true;}) // trace
.map(n -> Math.abs(n))
.filter(w -> {System.out.print(" " + w); return true;}) // trace
.filter(n -> n % 2 == 0)
.distinct()
.sum();
System.out.println(sum);
Close, but that's not quite it, as it doesn't have the proper delimiters to make it legible:
3 3 -4 4 8 8 4 4 -2 2 17 17 9 9 -10 10 14 14 6 6 -12 1256
What I want is:
[3, -4, 8, 4, -2, 17, 9, -10, 14, 6, -12]
[3 4 8 4 2 17 9 10 14 6 12]
56
Is there a more standard way to do this? Note that Peek as the linked article says does not do this, because I somehow want to collect all the elements of the stream at each stage together.
You'd need to use a different list for each checkpoint.
Normally I would not recommend using stream operations to mutate state in this way, but for debugging purposes I think it's ok. In fact, as @BrianGoetz points out below, debugging was the reason why peek
was added.
int[] nums = {3, -4, 8, 4, -2, 17, 9, -10, 14, 6, -12};
List<Integer> checkPoint1 = new ArrayList<>();
List<Integer> checkPoint2 = new ArrayList<>();
List<Integer> checkPoint3 = new ArrayList<>();
List<Integer> checkPoint4 = new ArrayList<>();
int sum = Arrays.stream(nums)
.peek(checkPoint1::add)
.map(n -> Math.abs(n))
.peek(checkPoint2::add)
.filter(n -> n % 2 == 0)
.peek(checkPoint3::add)
.distinct()
.peek(checkPoint4::add)
.sum();
System.out.println(checkPoint1);
System.out.println(checkPoint2);
System.out.println(checkPoint3);
System.out.println(checkPoint4);
System.out.println(sum);
Output:
[3, -4, 8, 4, -2, 17, 9, -10, 14, 6, -12]
[3, 4, 8, 4, 2, 17, 9, 10, 14, 6, 12]
[4, 8, 4, 2, 10, 14, 6, 12]
[4, 8, 2, 10, 14, 6, 12]
56
If you find yourself needing Paul's solution very often then one could write a static wrapper function that generates indexed peek functions:
int sum = streamDebugger<Integer>((Supplier<Consumer<?>> peekGen) => {
return Arrays.stream(nums)
.peek(peekGen.get())
.map(n -> Math.abs(n))
.peek(peekGen.get())
.filter(n -> n % 2 == 0)
.peek(peekGen.get())
.distinct()
.peek(peekGen.get())
.sum();
})
To automate this one step further one could even wrap the entire stream interface(s) so that each defined interface method again returns a wrapped implementation that automatically does the peeking for each step in the pipeline:
int sum = debugIntStream(Arrays.stream(nums))
.map(n -> Math.abs(n))
.filter(n -> n % 2 == 0)
.distinct()
.sum();
Since the implementation would be fairly tedious and hopefully not necessary the implementation is left to the reader.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With