web-dev-qa-db-fra.com

Comment insérer un compteur dans un 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();
    }

J'ai besoin d'écrire la ligne avec le numéro de ligne, j'ai donc essayé d'ajouter un compteur dans le .forEach (), mais je ne peux pas le faire fonctionner. Je ne sais juste pas où mettre l'i ++; dans le code, visser au hasard n'a pas aidé jusqu'à présent.

23
Vega

Ceci est un bon exemple où vous devriez plutôt utiliser une bonne boucle à l'ancienne. Alors que Files.lines() donne spécifiquement un flux séquentiel, les flux peuvent être produits et traités dans le désordre, donc insérer des compteurs et s'appuyer sur leur ordre est une assez mauvaise habitude. Si vous voulez toujours le faire, n'oubliez pas que partout où vous pouvez utiliser un lambda, vous pouvez toujours utiliser une classe anonyme complète. Les classes anonymes sont des classes normales et peuvent en tant que telles avoir un état.

Donc, dans votre exemple, vous pourriez faire comme ceci:

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

Vous pouvez utiliser un AtomicInteger comme compteur final mutable.

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

Comme indiqué dans Java doc :

Toute variable locale, paramètre formel ou paramètre d'exception utilisé mais non déclaré dans une expression lambda doit être déclaré final ou effectivement définitif (§4.12.4), ou une erreur de compilation se produit lorsque l'utilisation est tentée.

Cela signifie que votre variable doit être finale ou effectivement définitive. Vous voulez ajouter un compteur dans forEach et pour cela vous pouvez utiliser un AtomicInteger comme suggéré par OldCurumudgeon qui est IMO une approche préférée.

Je pense que vous pouvez également utiliser un tableau ayant une seule valeur 0 que vous pouvez utiliser comme compteur. Vérifiez et faites-moi savoir si l'exemple suivant vous convient:

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