How to debug a stream (). Map (...) with lambda expressions?

In our project, we are switching to java 8, and we are testing its new features.

In my project, I use Guava predicates and functions to filter and transform some collections using Collections2.transform and Collections2.filter .

In this migration, I need to change, for example, the guava code to java 8 changes. So the changes I make are as follows:

 List<Integer> naturals = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10,11,12,13); Function <Integer, Integer> duplicate = new Function<Integer, Integer>(){ @Override public Integer apply(Integer n) { return n * 2; } }; Collection result = Collections2.transform(naturals, duplicate); 

For...

 List<Integer> result2 = naturals.stream() .map(n -> n * 2) .collect(Collectors.toList()); 

Using guava I was very convenient for debugging the code, since I could debug every conversion process, but my problem is how to debug, for example .map(n -> n*2) .

Using the debugger, I see code like:

 @Hidden @DontInline /** Interpretively invoke this form on the given arguments. */ Object interpretWithArguments(Object... argumentValues) throws Throwable { if (TRACE_INTERPRETER) return interpretWithArgumentsTracing(argumentValues); checkInvocationCounter(); assert(arityCheck(argumentValues)); Object[] values = Arrays.copyOf(argumentValues, names.length); for (int i = argumentValues.length; i < values.length; i++) { values[i] = interpretName(names[i], values); } return (result < 0) ? null : values[result]; } 

But it is not as scary as Guava for debugging code, in fact I could not find the n * 2 transform.

Is there a way to see this conversion or a way to easily debug this code?

EDIT: I added an answer from various comments and posted the answers

Thanks to a comment by Holger that answered my question, the approach with the lambda block allowed me to see the transformation process and debug what happened inside the lambda body:

 .map( n -> { Integer nr = n * 2; return nr; } ) 

Thanks to Stuart Marks method-referenced approach also allowed me to debug the conversion process:

 static int timesTwo(int n) { Integer result = n * 2; return result; } ... List<Integer> result2 = naturals.stream() .map(Java8Test::timesTwo) .collect(Collectors.toList()); ... 

Thanks to Marlon Bernardes answer, I noticed that my Eclipse does not show what it needs, and using peek () helped display the results.

+101
java debugging lambda java-8
Jul 02 '14 at
source share
4 answers

I usually have no problems debugging lambda expressions when using Eclipse or IntelliJ IDEA. Just set a breakpoint and make sure you don't check all lambda expressions (check only lambda body).

Debugging lambdas

Another approach is to use peek to check the elements of a stream:

 List<Integer> naturals = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12,13); naturals.stream() .map(n -> n * 2) .peek(System.out::println) .collect(Collectors.toList()); 

UPDATE:

I think you are confused because map is an intermediate operation other words: it is a lazy operation that will only be performed after the terminal operation . Therefore, when you call stream.map(n β†’ n * 2) lambda body is not currently running. You need to set a breakpoint and check it after calling the terminal operation (in this case, collect ).

Check out Stream Operations for further explanations.

UPDATE 2:

I quote Holger's comment:

What makes this tricky is that the map call and the lambda expression are on the same line, so the line breakpoint stops at two completely unrelated actions.

Inserting a line break immediately after map( allows you to set a breakpoint only for a lambda expression. And it’s not unusual for debuggers to show intermediate values ​​for the return . Changing the lambda to n β†’ { int result=n * 2; return result; } allows you to check the result. Again, insert line breaks when moving from line to line ...

+74
Jul 02 '14 at 22:33
source share

IntelliJ has such a good plugin for this case as the Java Stream Debugger plugin. You should check this: https://plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform=hootsuite

It expands the IDEA Debugger window by adding the Trace Current Stream Chain button, which becomes active when the debugger stops within the Stream API call chain.

It has a nice interface for working with individual thread operations and gives you the ability to follow some values ​​that you need to debug.

Java Stream Debugger

You can start it manually from the debug window by clicking here:

enter image description here

+27
Jun 11 '17 at 6:46
source share

Debugging lambdas also works well with NetBeans. I am using NetBeans 8 and JDK 8u5.

If you set a breakpoint on a line where there is a lambda, you will click once when the pipeline is configured, and then once for each flow element. Using your example, the first click on the breakpoint will call map() , which sets up the stream pipeline:

first breakpoint

You can see the call stack and local variables and parameter values ​​for main , as you would expect. If you continue with the step, the "same" breakpoint will be deleted again, except that it will be within the lambda call:

enter image description here

Note that this time, the call stack is deep inside the thread mechanism, and local variables are the locales of the lambda itself, not the main method that spans. (I changed the values ​​in the naturals list to make this clear.)

As Marlon Bernardez pointed out (+1), you can use peek to check the values ​​as they go through. Be careful if you use this from a parallel thread. Values ​​can be printed in unpredictable order for different streams. If you store values ​​in a peek debug data structure, this data structure should of course be thread safe.

Finally, if you are doing a lot of debugging of lambdas (especially the multi-line lambdas operator), it might be preferable to extract the lambda into the named method and then access it using the method reference. For example,

 static int timesTwo(int n) { return n * 2; } public static void main(String[] args) { List<Integer> naturals = Arrays.asList(3247,92837,123); List<Integer> result = naturals.stream() .map(DebugLambda::timesTwo) .collect(toList()); } 

This can make it easier to see what happens during debugging. In addition, extracting methods in this way makes unit testing easier. If your lambda is so complex that you need to go through it in one step, you probably want to have a bunch of unit tests for it.

+21
Jul 03 '14 at 3:55
source share

Intellij IDEA 15 seems to make it even easier, it allows you to stop in the part of the line where the lambda is located, see the first function: http://blog.jetbrains.com/idea/2015/06/intellij-idea-15 -eap -is open /

+6
Aug 28 '15 at 7:58
source share



All Articles