如何用lambdaexpression式debuggingstream()。map(…)?

在我们的项目中,我们正在迁移到Java 8,我们正在testing它的新function。

在我的项目中,我使用Guava谓词和函数来使用Collections2.transformCollections2.filter来过滤和转换一些集合。

在这个迁移我需要改变例如番石榴代码到Java 8的变化。 所以,我正在做的改变是这样的:

 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); 

至…

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

使用番石榴我非常舒适的debugging代码,因为我可以debugging每个转换过程,但我关心的是如何debugging例如.map(n -> n*2)

使用debugging器,我可以看到一些代码:

 @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]; } 

但是它不像Guava那样直接debugging代码,实际上我找不到n * 2转换。

有没有办法看到这个转换或简单的debugging这个代码的方法?

编辑:我添加了来自不同意见的答案,并张贴了答案

感谢Holger回答我的问题的评论,使用lambda块的方法允许我看到转换过程并debugginglambda体内发生的事情:

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

感谢Stuart Marks方法引用的方法也允许我debugging转换过程:

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

感谢Marlon Bernardes回答,我注意到我的Eclipse没有显示它应该和peek()的用法帮助显示结果。

在使用Eclipse Kepler或Intellij IDEA(使用JDK8u5)时,我通常没有问题debugginglambdaexpression式。 只要设置一个断点,一定不要检查整个lambdaexpression式(只检查lambda体)。

调试Lambda

另一种方法是使用peek来检查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()); 

更新:

我认为你会感到困惑,因为map是一个intermediate operation – 换句话说,这是一个懒惰的操作,只有在terminal operation执行后才会执行。 所以当你调用stream.map(n -> n * 2) ,lambda体不会被执行。 您需要设置一个断点并在terminal操作被调用后检查它(在这种情况下collect )。

检查stream操作进一步的解释。

更新2:

引用Holger的评论:

这里棘手的地方在于map和lambdaexpression式的调用是在一行中,所以行断点将会停在两个完全不相关的动作上。

map(之后插入一个换行符map(允许为lambdaexpression式设置一个断点,debugging器不会显示return语句的中间值并不常见,将lambda改为n -> { int result=n * 2; return result; }将允许您检查结果。

debugginglambda也适用于NetBeans。 我正在使用NetBeans 8和JDK 8u5。

如果你在一个lambdaexpression式的行上设置了一个断点,那么你实际上会在stream水线设置时点击一次,然后在每个stream元素上点击一次。 使用你的例子,你第一次打断点将是设置streampipe线的map()调用:

第一个断点

你可以看到调用堆栈以及main的局部variables和参数值。 如果继续步进,则会再次触发“相同”断点,除了这次是在调用lambda的时候:

在这里输入图像说明

请注意,这次调用堆栈在stream机制中很深,局部variables是lambda本身的局部variables,而不是封闭的main方法。 (我已经改变了naturals清单中的值,以使其清晰。)

正如Marlon Bernardes指出的那样(+1),你可以使用peek来检查值。 要小心,如果你使用这个从平行stream。 这些值可以在不同的线程中以不可预知的顺序打印。 如果您将查询的值存储在debugging数据结构中,则该数据结构当然必须是线程安全的。

最后,如果您正在进行大量的lambdadebugging(特别是多行语句lambdas),最好将lambda提取到一个已命名的方法中,然后使用方法引用来引用它。 例如,

 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()); } 

这可能会使您在debugging时更容易看到正在发生的事情。 另外,这种提取方法使得unit testing更容易。 如果你的lambda非常复杂,你需要单步执行它,那么你可能要为它做一堆unit testing。

IntelliJ在这种情况下有一个很好的插件,就像Java Stream Debugger插件一样。 我build议你看看这个: https : //plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform=hootsuite

它通过添加跟踪当前stream链button来扩展IDEAdebugging器工具窗口,该button在debugging器停止在Stream API调用链内时变为活动状态。

它具有很好的界面来处理单独的stream操作,并为您提供了一些机会,以遵循一些您应该debugging的值。

Java流调试器

Intellij IDEA 15似乎使它更容易,它允许停止在lambda所在行的一部分,请参阅第一个function: http : //blog.jetbrains.com/idea/2015/06/intellij-idea-15 -EAP-是开/