ラムダ式を使用してstream()。map(…)をデバッグする方法は?


114

私たちのプロジェクトでは、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。実際、変換を見つけることができませんでした。

この変換を確認する方法またはこのコードを簡単にデバッグする方法はありますか?

編集:私はさまざまなコメントと投稿された回答から回答を追加しました

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回答のおかげで、私のEclipseには何が必要かが表示されず、peek()の使用が結果の表示に役立ちました。


一時result変数をとして宣言する必要はありませんInteger。シンプルでint、あなたがしている場合にも行う必要がありmapピングintint...
ホルガー

また、IntelliJ IDEA 14には改善されたデバッガーがあることも追加しました。これで、ラムダをデバッグできます。
ミハイル

回答:


86

私は通常、EclipseまたはIntelliJ IDEAを使用しているときは、ラムダ式のデバッグに問題はありません。ブレークポイントを設定し、ラムダ式全体を検査しないようにしてください(ラムダ本体のみを検査します)。

ラムダのデバッグ

もう1つの方法は、を使用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:

Holgerのコメントを引用:

ここで注意が必要なのは、mapへの呼び出しとラムダ式が1行にあるため、行ブレークポイントが2つのまったく関係のないアクションで停止することです。

直後に改行を挿入するmap( と、ラムダ式にのみブレークポイントを設定できます。また、デバッガーがreturnステートメントの中間値を表示しないことも珍しくありません。ラムダをに変更すると、n -> { int result=n * 2; return result; } 結果を検査できます。繰り返しますが、行ごとにステップするときに改行を適切に挿入します…


印刷画面をありがとう。Eclipseのどのバージョンを持っていますか、またはそのダイアログを取得するために何をしましたか?私が使用してみましたinspectdisplay して取得しますn cannot be resolved to a variable。ところで、peekも便利ですが、すべての値を一度に出力します。変換をチェックするために、各反復プロセスを見たいです。可能ですか?
Federico Piazza 14

私はEclipse Kepler SR2を使用しています(EclipseのマーケットプレイスからJava 8サポートがインストールされています)。
Marlon Bernardes 14

あなたもEclipseを使っていますか?.map行にブレークポイントを設定し、F8キーを複数回押すだけです。
Marlon Bernardes 14

6
@Fede:ここで注意が必要なのは、呼び出しmapとラムダ式が1行にあるため、行ブレークポイントが2つのまったく関係のないアクションで停止することです。直後に改行を挿入するmap(と、ラムダ式にのみブレークポイントを設定できます。また、デバッガーがreturnステートメントの中間値を表示しないことも珍しくありません。ラムダをに変更n -> { int result=n * 2; return result; }すると、検査できますresult。繰り返しますが、行ごとにステップ実行するときに改行を適切に挿入します...
Holger

1
@Marlon Bernardes:確かに、それはコメントの目的なので、答えに追加できます。コンテンツの改善に役立ちます。ところで、引用符で囲まれたテキストを編集してコードの書式を追加しました…
Holger

33

IntelliJには、Javaストリームデバッガプラグインなど、このケースに最適なプラグインがあります。あなたはそれをチェックアウトする必要がありますhttps : //plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform=hootsuite

IDEAデバッガーツールウィンドウを拡張するには、[現在のストリームチェーンのトレース]ボタンを追加します。このボタンは、デバッガーがストリームAPI呼び出しのチェーンの内部で停止したときにアクティブになります。

個別のストリーム操作を操作するための優れたインターフェースがあり、デバッグする必要があるいくつかの値を追跡する機会が与えられます。

Javaストリームデバッガ

ここをクリックして、デバッグウィンドウから手動で起動できます。

ここに画像の説明を入力してください


23

ラムダのデバッグは、NetBeansでもうまく機能します。NetBeans 8とJDK 8u5を使用しています。

ラムダがある行にブレークポイントを設定すると、実際には、パイプラインが設定されたときに1回、次に各ストリーム要素に対して1回ヒットします。この例を使用すると、ブレークポイントに初めて到達したときにmap()、ストリームパイプラインを設定する呼び出しになります。

最初のブレークポイント

main期待どおりに、コールスタックとローカル変数およびパラメーター値を確認できます。ステップ実行を続けると、「同じ」ブレークポイントが再度ヒットしますが、今回はラムダの呼び出し内にあります。

ここに画像の説明を入力してください

今回は、コールスタックがストリームメカニカル内の深い場所にあり、ローカル変数はラムダ自体のローカルであり、囲んでいるmainメソッドではないことに注意してください。(naturalsこれを明確にするために、リストの値を変更しました。)

以下のようマーロンBernardesは、(1)を指摘し、あなたが使用することができpeek、彼らはパイプラインのことで行くような値を検査します。ただし、これを並列ストリームから使用する場合は注意してください。値は、異なるスレッド間で予測できない順序で出力される可能性があります。からデバッグデータ構造に値を格納する場合peek、そのデータ構造はもちろんスレッドセーフでなければなりません。

最後に、ラムダ(特に複数行のステートメントラムダ)の多くのデバッグを行っている場合は、ラムダを名前付きメソッドに抽出し、メソッド参照を使用して参照することをお勧めします。例えば、

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

これにより、デバッグ中に何が起こっているかを簡単に確認できます。さらに、この方法でメソッドを抽出すると、単体テストが簡単になります。ラムダが非常に複雑なので、シングルステップで実行する必要がある場合は、とにかくラムダの一連の単体テストが必要になるでしょう。


私の問題は、ラムダ本体をデバッグできないことでしたが、メソッド参照を使用するあなたのアプローチは、私が望んでいたことを大いに助けてくれました。{ int result=n * 2; return result; }別の行を追加することで完全に機能するHolgerアプローチを使用して回答を更新できます。両方の回答が役に立ったので、私は回答を受け入れることができました。もちろん+1。
Federico Piazza 14

1
@Fede他の回答はすでに更新されているようなので、私のものを更新する必要はありません。とにかく、複数行のラムダは嫌いです。:-)
スチュワートマークス

1
@Stuart Marks:私も単一行ラムダを好みます。そのため、通常、デバッグ後に改行を削除します。これは、「他の(通常の)複合ステートメントにも適用されます」。
Holger

1
@Fede心配ありません。それはあなたが好むどちらの答えを受け入れることも、質問者としてのあなたの特権です。+1をありがとう。
スチュアートマークス

1
メソッド参照を作成すると、メソッドを単体テストしやすくなるだけでなく、コードが読みやすくなります。正解です。(+1)
Marlon Bernardes 14


6

更新された詳細情報(2019年10月)を提供するためだけに、IntelliJは非常に便利なこのタイプのコードをデバッグするための非常に優れた統合を追加しました。

ラムダを含む行で停止するときにF7(ステップイン)を押すと、IntelliJはデバッグするスニペットを強調表示します。デバッグするチャンクを切り替えることができTabますF7。決定したら、もう一度クリックします。

ここにいくつかのスクリーンショットを示します:

1- F7(ステップイン)キーを押すと、ハイライト(または選択モード)が表示されます ここに画像の説明を入力してください

2- Tab複数回使用してデバッグするスニペットを選択します ここに画像の説明を入力してください

3- F7(ステップイン)キーを押してステップイン ここに画像の説明を入力してください


1

IDEを使用したデバッグは常に役立ちますが、ストリーム内の各要素をデバッグする理想的な方法は、Java Steamが遅延評価されるため、ターミナルメソッド操作の前にpeek()を使用することです。評価されません。

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());
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.