Death by boilerplate: Iterating over lines in a file in Java (vs Scala, Ruby, Groovy)

How to iterate over lines in a file in Java, Scala, Ruby and Groovy

Iterating over the lines of a small text file is a pretty common (and simple) operation. Given how routine this task is you would think that it would require minimal code. Java is quite famous for requiring lots of code to get the job done. Using the standard libraries in Java 6 we typically end up with code looking something like this:

Java 6:

     try {
            BufferedReader reader = new BufferedReader(new FileReader("blah.txt"));
            try {
                String line = null;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
            } finally {
                reader.close();
            }
        } catch (IOException ioe) {
            System.err.println("oops " + ioe.getMessage());
        }
    }

Every Java programmer has at one stage or another had to write code that looks a lot like this. I’ve probably written code looking a lot like this many, many times. Even for someone who can type very quickly it’s a lot of boilerplate. It’s not particularly complicated code, just very verbose. While there are 3rd party libraries (Apache commons,io) that make this much simpler many (most?) developers will rather just use the standard libs.

This may seem perfectly reasonable to a seasoned Java dev, but for anyone coming from Python, Ruby or even C# the amount of code required for this simple task seems excessive. Java 7 improves on this somewhat with ARM (Automatic Resource Management) and by providing utility methods in java.nio.file.Files. I was originally going to write a blog post about how useful this class is for doing file reads with minimal boilerplate code, but alas it still requires some extra plumbing. The readAllLines(Path path, Charset cs) method cuts down on much of the boilerplate. Unfortunately it still requires a path object to be created, along with a charset (no option to use the default on the local system). This results in code along these lines:

Java 7:

    try {
        for (String line : Files.readAllLines(new File("blah.txt").toPath(), Charset.forName("UTF-8"))) {
            System.out.println(line);
        }
    } catch (IOException ioe) {
        System.err.println("oops " + ioe.getMessage());
    }

If we compare this with virtually any well known modern language the difference is obvious. Consider for example the code required to perform this tasks in Scala, Ruby or Groovy:
Scala:

Source.fromFile("blah.txt").getLines.foreach { println }

Groovy:

new File("blah.txt").eachLine { line -> println(line) }

Ruby:

IO.foreach("blah.txt") { |line| puts line }

Notice how much less boilerplate is involved. It only takes one line of code to perform the task. The question which needs to be asked is this: after more than 15 years, reading from a file in Java is still more painful than virtually any other modern language. This would be easy to fix by providing sensible methods with defaults. Imagine something more like:

    try {
        for (String line : Files.readAllLines("blah.txt")) { //what the method would look like with a simpler interface...
            System.out.println(line);
        }
    } catch (IOException ioe) {
        System.err.println("oops " + ioe.getMessage());
    }

Please Oracle, add something sensible like this to JDK 8.

Advertisements

4 thoughts on “Death by boilerplate: Iterating over lines in a file in Java (vs Scala, Ruby, Groovy)

  1. try {
    for(Scanner scanner = new Scanner( new File(“blah.txt”) ); scanner.hasNextLine();)
    System.out.println(scanner.nextLine());
    }
    catch(FileNotFoundException ioe) {
    System.err.println(“oops ” + ioe.getMessage());
    }

    Since Java 5…

    • Thanks Steve. I’d forgotten about Scanner, haven’t used it in a while. It’s a slight improvement over the jdk 6 example I gave, but about on par with the jdk 7 example in terms of verbosity.

  2. To keep the language equivalency closer, the Java code should just throw the exception altogether. “throws IOException”. That’s what the others do, right? The other question is how the others handle an actual UTF-8 file. You know, one containing Unicode codepoints that consume more than eight bytes when encoded in UTF-8. Just to keep everything fair.

    The need to call toPath() is certainly weird, though. Why not a readAllLines that took a File?

    • A fair point regarding the exceptions, but even without the exception handling the Java is still significantly more verbose. As for character encoding there should be a sensible default with an option to override it where necessary. The Files class forces the programmer to specify this even for cases where it isn’t necessary. Also, why the need for a Path object when a String will do?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s