如何用lambdaexpression式debuggingstream()。map(…)?
在我们的项目中,我们正在迁移到Java 8,我们正在testing它的新function。
在我的项目中,我使用Guava谓词和函数来使用Collections2.transform
和Collections2.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体)。
另一种方法是使用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的值。
Intellij IDEA 15似乎使它更容易,它允许停止在lambda所在行的一部分,请参阅第一个function: http : //blog.jetbrains.com/idea/2015/06/intellij-idea-15 -EAP-是开/