There is no elegant functional solution to a non-functional task. The first you may consider, is just resorting to an ordinary anonymous inner class:
String path = "/tmp/timeline.txt";
try(Stream<String> lines = Files.lines(Paths.get(path), Charset.defaultCharset())) {
lines.limit(10).forEachOrdered(new Consumer<String>() {
int counter = 0;
public void accept(String line) {
System.out.println("Line " + counter++ + ": " + line.trim());
}
});
} catch (IOException e) {
e.printStackTrace();
}
The advantage is that it doesn’t pretend to be functional where it isn’t and the scope of the counter
variable has the smallest scope needed for this task.
If you are going to do more than just-printing these numbered lines and need a solution compatible with all stream operations, re-implementing the stream source is a straight-forward solution:
static Stream<String> numberedLines(Path path, Charset cs) throws IOException {
BufferedReader br = Files.newBufferedReader(path, cs);
return StreamSupport.stream(new Spliterators.AbstractSpliterator<String>(
Long.MAX_VALUE, Spliterator.ORDERED|Spliterator.NONNULL) {
int counter;
public boolean tryAdvance(Consumer<? super String> action) {
String line;
try {
line = br.readLine();
if(line==null) return false;
action.accept("Line " + counter++ + ": " + line.trim());
return true;
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
}, true).onClose(()->{ try { br.close(); }
catch (IOException ex) { throw new UncheckedIOException(ex); }
});
}
Of course, this isn’t as simple as a single lambda expression but using this reusable method, you can use all stream operations without problems, e.g.
String path = "/tmp/timeline.txt";
try(Stream<String> lines = numberedLines(Paths.get(path), Charset.defaultCharset())) {
lines.skip(10).limit(10).forEachOrdered(System.out::println);
} catch(IOException e) {
e.printStackTrace();
}