mechanikos mechanikos - 1 month ago 5
Java Question

Is there any way for reading two or more files in one Java8-stream?

I like new Java8 StreamAPI and want use it not only for one file.
As usually, I use this code:

Stream<String> lines = Files.lines(Paths.get("/somepathtofile"));


But how read two file in one stream if it possibly?

Answer

Without any extra helper functions or outside libraries, the easiest is:

Stream<String> lines1 = Files.lines(Paths.get("/somepathtofile"));
Stream<String> lines2 = Files.lines(Paths.get("/somepathtoanotherfile"));

Stream.concat(lines1, lines)
    .filter(...)
    .forEach(...);

If Files.lines hadn't been declared to throw a checked exception, you'd be able to do

Stream.of("/file1", "/file2")
     .map(Paths::get)
     .flatMap(Files::lines)....

But, alas, we can't do that. There are several workarounds. One is to make your own version of Files.lines that calls the standard one, catches IOException and rethrows as an UncheckedIOException. Another approach is a more general way to make functions out of methods that throw checked exceptions. It would look something like this:

@FunctionalInterface
public interface ThrowingFunction<T,R> extends Function<T,R> {

    @Override
    public default R apply(T t) {
        try {
            return throwingApply(t);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static<T,R> Function<T,R> wrap(ThrowingFunction<T,R> f) {
        return f;
    }

    R throwingApply(T t) throws Exception;
}

and then

Stream.of("/somefile", "/someotherfile", "/yetanotherfile")
        .map(Paths::get)
        .flatMap(ThrowingFunction.wrap(Files::lines))
        .....

There are several libraries out there that went through the trouble of writing something like the above out for every functional interface.