Как отлаживать поток ().карта(...) с лямбда-выражениями?



в нашем проекте мы переходим на java 8 и тестируем его новые возможности.



в моем проекте я использую предикаты и функции Guava для фильтрации и преобразования некоторых коллекций с помощью Collections2.transform и Collections2.filter.



в этой миграции мне нужно изменить, например, код guava на изменения 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 трансформация.



есть ли способ увидеть это преобразование или способ простой отладки этого кода?



EDIT: я добавил ответ из разных комментариев и опубликовал ответы



спасибо 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());
...


спасибо Marlon Bernardes ответ я заметил, что мое затмение не показывает, что оно должно, и использование peek () помогло для отображения результатов.

764   4  

4 ответов:

обычно у меня нет проблем с отладкой лямбда-выражений при использовании Eclipse Kepler или Intellij IDEA (используя JDK8u5). Просто установите точку останова и не проверяйте все лямбда-выражение (проверяйте только тело лямбды).

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

обновление:

Я думаю, что вы путаетесь, потому что map это intermediate operation - другими словами: это медленная операция, которая будет выполняться только после terminal operation был казнен. Поэтому, когда вы звоните stream.map(n -> n * 2) лямбда-тело в данный момент не выполняется. Необходимо установить точку останова и проверить ее после вызова терминальной операции (collect в данном случае).

Регистрация Поток Операций для дальнейшего объяснения.

обновление 2:

цитирую Хольгер-х комментарий:

что делает его сложным здесь является то, что вызов карты и лямбда выражение находится в одной строке, поэтому точка останова строки остановится на двух совершенно несвязанные действия.

вставка разрыва строки после map( позволит вам установить точку останова только для лямбда-выражения. И это не редкость, что отладчики не показывают промежуточные значения а return заявление. Изменение лямбда на n -> { int result=n * 2; return result; } позволит вам проверить результат. Опять же, вставьте строку разрывы соответствующим образом при переходе строка за строкой...

IntelliJ имеет такой хороший плагин для этого случая, как Java Stream Debugger плагин. Я предлагаю вам посмотреть на это: https://plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform=hootsuite

Он расширяет окно инструментов IDEA Debugger, добавляя кнопку трассировки текущей цепочки потоков, которая становится активной, когда отладчик останавливается внутри цепочки вызовов API потока.

Он имеет приятный интерфейс для работы с отдельными потоки операций и дает вам возможность следовать некоторым значениям, которые вы должны отлаживать.

Java Stream Debugger

отладка lambdas также хорошо работает с NetBeans. Я использую NetBeans 8 и JDK 8u5.

если вы установите точку останова на линии, где есть лямбда, вы на самом деле ударите один раз, когда трубопровод настроен, а затем один раз для каждого элемента потока. Используя Ваш пример, первый раз, когда вы нажмете точку останова будет map() вызов, который настраивает конвейер потока:

first breakpoint

вы можете увидеть стек вызовов и локальные переменные и параметр значения для main как и следовало ожидать. Если вы продолжите шагать," та же " точка останова будет снова нажата, за исключением этого времени, когда она находится в вызове лямбда:

enter image description here

обратите внимание, что на этот раз стек вызовов находится глубоко внутри машины потоков, а локальные переменные являются локальными объектами самой лямбды, а не заключающими main метод. (Я изменил значения в naturals список для ясности.)

как Марлон Бернардис указал, (+1), Вы можете использовать peek для проверки значений по мере их прохождения в конвейере. Будьте осторожны, если вы используете это из параллельного потока. Значения могут быть напечатаны в произвольном порядке в разных потоках. Если вы храните значения в отладочной структуре данных от peek, эта структура данных, конечно, должна быть потокобезопасной.

наконец, если вы делаете много отладки лямбд (особенно многострочный оператор lambdas), может быть предпочтительнее извлечь лямбда в именованный метод, а затем ссылаться на него с помощью ссылки на метод. Например,

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

это может сделать его легче увидеть, что происходит во время отладки. Кроме того, методы извлечения таким образом упрощают модульное тестирование. Если ваша лямбда настолько сложна, что вам нужно пройти через нее один шаг, вы, вероятно, захотите иметь кучу модульных тестов для нее в любом случае.

Intellij IDEA 15 кажется еще проще, он позволяет остановиться в той части строки, где находится лямбда, см. первую функцию:http://blog.jetbrains.com/idea/2015/06/intellij-idea-15-eap-is-open/

Comments

    Ничего не найдено.