Java 如何使用 lambda 表达式调试 stream().map(...)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24541786/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 12:48:26  来源:igfitidea点击:

How to debug stream().map(...) with lambda expressions?

javadebugginglambdajava-8

提问by Federico Piazza

In our project we are migrating to java 8 and we are testing the new features of it.

在我们的项目中,我们正在迁移到 Java 8 并测试它的新功能。

On my project I'm using Guava predicates and functions to filter and transform some collections using Collections2.transformand Collections2.filter.

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

On this migration I need to change for example guava code to java 8 changes. So, the changes I'm doing are the kind of:

在这次迁移中,我需要将例如番石榴代码更改为 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);

To...

到...

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

Using guava I was very confortable debugging the code since I could debug each transformation process but my concern is how to debug for example .map(n -> n*2).

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

Using the debugger I can see some 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 isn't as straighforward as Guava to debug the code, actually I couldn't find the n * 2transformation.

但是调试代码并没有Guava那么直接,实际上我找不到n * 2转换。

Is there a way to see this transformation or a way to easy debug this code?

有没有办法查看这种转换或轻松调试此代码?

EDIT: I've added answer from different comments and posted answers

编辑:我添加了来自不同评论的答案并发布了答案

Thanks to Holgercomment that answered my question, the approach of having lambda block allowed me to see the transformation process and debug what happened inside lambda body:

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

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

Thanks to Stuart Marksthe approach of having method references also allowed me to debug the transformation process:

由于Stuart Marks使用方法引用的方法还允许我调试转换过程:

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 Bernardesanswer I noticed that my Eclipse doesn't show what it should and the usage of peek() helped to display results.

感谢Marlon Bernardes回答,我注意到我的 Eclipse 没有显示它应该显示的内容,并且 peek() 的使用有助于显示结果。

采纳答案by Marlon Bernardes

I usually have no problem debugging lambda expressions while using Eclipse or IntelliJ IDEA. Just set a breakpoint and be sure not to inspect the whole lambda expression (inspect only the lambda body).

我在使用 Eclipse 或 IntelliJ IDEA 时调试 lambda 表达式通常没有问题。只需设置一个断点,并确保不要检查整个 lambda 表达式(仅检查 lambda 主体)。

Debugging Lambdas

调试 Lambda

Another approach is to use peekto inspect the elements of the stream:

另一种方法是用于peek检查流的元素:

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're getting confused because mapis an intermediate operation- in other words: it is a lazy operation which will be executed only after a terminal operationwas executed. So when you call stream.map(n -> n * 2)the lambda body isn't being executed at the moment. You need to set a breakpoint and inspect it after a terminal operation was called (collect, in this case).

我认为您会感到困惑,因为mapintermediate operation- 换句话说:它是一个惰性操作,只有在terminal operation执行a 后才会执行。因此,当您调用stream.map(n -> n * 2)lambda 时,此时主体并未执行。您需要设置断点并在调用终端操作后对其进行检查(collect在本例中为 )。

Check Stream Operationsfor further explanations.

检查流操作以获得进一步的解释。

UPDATE 2:

更新 2:

Quoting Holger'scomment:

引用Holger 的评论:

What makes it tricky here is that the call to map and the lambda expression are in one line so a line breakpoint will stop on two completely unrelated actions.

Inserting a line break right after map(would allow you to set a break point for the lambda expression only. And it's not unusual that debuggers don't show intermediate values of a returnstatement. Changing the lambda to n -> { int result=n * 2; return result; }would allow you to inspect result. Again, insert line breaks appropriately when stepping line by line…

这里的棘手之处在于对 map 的调用和 lambda 表达式在一行中,因此行断点将在两个完全不相关的操作上停止。

立即插入换行符map(将允许您仅为 lambda 表达式设置断点。调试器不显示return语句的中间值并不罕见。将 lambda 更改为n -> { int result=n * 2; return result; }将允许您检查结果。再次,在逐行步进时适当插入换行符......

回答by Stuart Marks

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

调试 lambdas 也适用于 NetBeans。我正在使用 NetBeans 8 和 JDK 8u5。

If you set a breakpoint on a line where there's a lambda, you actually will hit once when the pipeline is set up, and then once for each stream element. Using your example, the first time you hit the breakpoint will be the map()call that's setting up the stream pipeline:

如果在有 lambda 的行上设置断点,则实际上会在设置管道时命中一次,然后为每个流元素命中一次。使用您的示例,您第一次遇到断点将是map()设置流管道的调用:

first breakpoint

第一个断点

You can see the call stack and the local variables and parameter values for mainas you'd expect. If you continue stepping, the "same" breakpoint is hit again, except this time it's within the call to the lambda:

您可以main按预期看到调用堆栈以及局部变量和参数值。如果继续单步执行,“相同”断点将再次被击中,但这次它在对 lambda 的调用中:

enter image description here

在此处输入图片说明

Note that this time the call stack is deep within the streams machinery, and the local variables are the locals of the lambda itself, not the enclosing mainmethod. (I've changed the values in the naturalslist to make this clear.)

请注意,这次调用堆栈位于流机制的深处,局部变量是 lambda 本身的局部变量,而不是封闭main方法。(我已经更改了naturals列表中的值以说明这一点。)

As Marlon Bernardespointed out (+1), you can use peekto inspect values as they go by in the pipeline. Be careful though if you're using this from a parallel stream. The values can be printed in an unpredictable order across different threads. If you're storing values in a debugging data structure from peek, that data structure will of course have to be thread-safe.

正如Marlon Bernardes指出的那样 (+1),您可以peek用来检查管道中经过的值。但是,如果您从并行流中使用它,请小心。这些值可以跨不同线程以不可预测的顺序打印。如果您将值存储在来自 的调试数据结构中peek,那么该数据结构当然必须是线程安全的。

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

最后,如果您要对 lambda 进行大量调试(尤其是多行语句 lambda),最好将 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());
}

This might make it easier to see what's going on while you're debugging. In addition, extracting methods this way makes it easier to unit test. If your lambda is so complicated that you need to be single-stepping through it, you probably want to have a bunch of unit tests for it anyway.

这可能会使您在调试时更容易看到发生了什么。此外,以这种方式提取方法可以更容易地进行单元测试。如果您的 lambda 如此复杂以至于您需要单步执行它,那么无论如何您可能希望对其进行大量单元测试。

回答by Egor

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

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

回答by Dmytro Melnychuk

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

IntelliJ 为这种情况提供了一个很好的插件,例如Java Stream Debugger插件。你应该检查一下:https: //plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform=hoosuite

It extends the IDEA Debugger tool window by adding the Trace Current Stream Chain button, which becomes active when debugger stops inside of a chain of Stream API calls.

它通过添加 Trace Current Stream Chain 按钮扩展了 IDEA Debugger 工具窗口,当调试器在 Stream API 调用链内停止时,该按钮会变为活动状态。

It has nice interface for working with separate streams operations and gives you opportunity to follow some values that u should debug.

它有很好的界面来处理单独的流操作,并让您有机会遵循一些您应该调试的值。

Java Stream Debugger

Java 流调试器

You can launch it manually from the Debug window by clicking here:

您可以通过单击此处从调试窗口手动启动它:

enter image description here

在此处输入图片说明

回答by Federico Piazza

Just to provide more updated details (Oct 2019), IntelliJ has added a pretty nice integration to debug this type of code that is extremely useful.

只是为了提供更多更新的细节(2019 年 10 月),IntelliJ 添加了一个非常好的集成来调试这种非常有用的代码。

When we stop at a line that contains a lambda if we press F7(step into) then IntelliJ will highlight what will be the snippet to debug. We can switch what chunk to debug with Taband once we decided it then we click F7again.

当我们在包含 lambda 的行处停止时,如果我们按下F7(step into),那么 IntelliJ 将突出显示要调试的代码段。我们可以切换要调试的块Tab,一旦我们决定它,然后我们F7再次单击。

Here some screenshots to illustrate:

这里有一些截图来说明:

1- Press F7(step into) key, will display the highlights (or selection mode) enter image description here

1- 按F7(步入)键,将显示高光(或选择模式) 在此处输入图片说明

2- Use Tabmultiple times to select the snippet to debug enter image description here

2-Tab多次使用以选择要调试的代码段 在此处输入图片说明

3- Press F7(step into) key to step into enter image description here

3- 按F7(step into) 键进入 在此处输入图片说明

回答by Sashank Samantray

Debugging using IDE's are always-helpful, but the ideal way of debugging through each elements in a stream is to use peek() before a terminal method operation since Java Steams are lazily evaluated, so unless a terminal method is invoked, the respective stream will not be evaluated.

使用 IDE 进行调试总是有帮助的,但调试流中每个元素的理想方法是在终端方法操作之前使用 peek(),因为 Java Steam 是惰性求值的,因此除非调用终端方法,否则相应的流将不予评价。

List<Integer> numFromZeroToTen = Arrays.asList(1,2,3,4,5,6,7,8,9,10);

    numFromZeroToTen.stream()
        .map(n -> n * 2)
        .peek(n -> System.out.println(n))
        .collect(Collectors.toList());