Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tracing Streams

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.

like image 747
pitosalas Avatar asked Apr 09 '16 20:04

pitosalas


2 Answers

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
like image 114
Paul Boddington Avatar answered Sep 25 '22 01:09

Paul Boddington


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.

like image 20
the8472 Avatar answered Sep 26 '22 01:09

the8472