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

2022-08-31 08:07:00

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

在我的项目中,我使用番石榴谓词和函数来过滤和转换一些使用 和 .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());

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

使用调试器,我可以看到一些代码,例如:

@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那样直截了当,实际上我找不到转换。n * 2

有没有办法看到这个转换或一种方法来轻松调试这个代码?

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

感谢回答我问题的评论,拥有lambda块的方法使我能够看到转换过程并调试lambda body内部发生的事情:Holger

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

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

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

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


答案 1

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

Debugging Lambdas

另一种方法是用于检查流的元素: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());

更新:

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

请查看流操作以获取进一步说明。

更新 2:

引用霍尔格的评论:

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

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


答案 2

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

它通过添加“跟踪当前流链”按钮来扩展 IDEA 调试器工具窗口,该按钮在调试器停止在流 API 调用链内时变为活动状态。

它具有用于处理单独流操作的漂亮界面,并让您有机会遵循您应该调试的一些值。

Java Stream Debugger

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

enter image description here