web-dev-qa-db-ja.com

Stream <String> .forEach()にカウンターを挿入する方法は?

FileWriter writer = new FileWriter(output_file);
    int i = 0;

    try (Stream<String> lines = Files.lines(Paths.get(input_file))) {
        lines.forEach(line -> {
            try {
                writer.write(i + " # " + line + System.lineSeparator());
            } catch (Exception e) {
                e.printStackTrace();
            }   
        }
                    );
        writer.close();
    }

行番号で行を記述する必要があるため、.forEach()にカウンターを追加しようとしましたが、機能させることができません。 i ++をどこに置くかわからないだけです。コードにランダムにねじ込むことは、これまでのところ役に立ちませんでした。

23
Vega

これは、古き良きforループを使用する必要がある場合の良い例です。 Files.lines()は具体的には連続ストリームを提供しますが、ストリームは順序どおりに生成および処理できるため、カウンターを挿入して順序に依存することはやや悪い習慣です。それでもやりたい場合は、ラムダを使用できる場所ならどこでも完全な匿名クラスを使用できることに注意してください。匿名クラスは通常のクラスであり、そのような状態を持つことができます。

したがって、あなたの例では次のようにすることができます:

FileWriter writer = new FileWriter(output_file);

try (Stream<String> lines = Files.lines(Paths.get(input_file))) {
    lines.forEach(new Consumer<String>() {
        int i = 0;
        void accept(String line) {
            try {
                writer.write((i++) + " # " + line + System.lineSeparator());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
    writer.close();
}
14
Erik Vesteraas

AtomicIntegerを可変finalカウンターとして使用できます。

public void test() throws IOException {
    // Make sure the writer closes.
    try (FileWriter writer = new FileWriter("OutFile.txt") ) {
        // Use AtomicInteger as a mutable line count.
        final AtomicInteger count = new AtomicInteger();
        // Make sure the stream closes.
        try (Stream<String> lines = Files.lines(Paths.get("InFile.txt"))) {
            lines.forEach(line -> {
                        try {
                            // Annotate with line number.
                            writer.write(count.incrementAndGet() + " # " + line + System.lineSeparator());
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
            );
        }
    }
}
47
OldCurmudgeon

Java doc に記載されているとおり:

使用されているがラムダ式で宣言されていないローカル変数、仮パラメータ、または例外パラメータは、最終宣言するか、実質的に最終宣言する必要があります(§4.12.4)。

これは、変数が最終または実質的に最終でなければならないことを意味します。カウンターをforEachに追加し、そのためにIMOが推奨されるアプローチであるOldCurumudgeonによって提案されたAtomicIntegerを使用できます。

値が1つだけの配列を使用することもできると思います0カウンタとして使用できます。次の例がうまくいくかどうかを確認してお知らせください。

public void test() throws IOException {
    FileWriter writer = new FileWriter("OutFile.txt");
    final int[] count = {0};

    try (Stream<String> lines = Files.lines(Paths.get("InFile.txt"))) {
        lines.forEach(line -> {
            try {
                count[0]++;
                writer.write(count[0] + " # " + line + System.lineSeparator());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        );
        writer.close();
    }
}
6
i_am_zero