How to insert a counter into a Stream<String> .forEach()?
Solution 1
This is a good example of where you should rather use a good old fashioned for loop. While Files.lines()
specifically gives a sequential stream, streams can be produced and processed out of order, so inserting counters and relying on their order is a rather bad habit to get into. If you still really want to do it, remember that anywhere you can use a lambda you can still use a full anonymous class. Anonymous classes are normal classes, and can as such have state.
So in your example you could do like this:
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();
}
Solution 2
You can use an AtomicInteger
as a mutable final
counter.
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();
}
}
);
}
}
}
Solution 3
As stated in Java doc:
Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.
It means your variable has to be final or effectively final. You want to add a counter into forEach
and for that you can use an AtomicInteger
as suggested by OldCurumudgeon which is IMO a preferred approach.
I believe you can also use an array having only one value 0
which you can use as counter. Check and let me know if the following example works for you:
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();
}
}
Comments
-
Vega over 3 years
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(); }
I need to write the line with the line number, so I tried to add a counter into the .forEach(), but I can't get it to work. I just don't know where to put the i++; into the code, randomly screwing around didn't help so far.