About Lukas Eder

Lukas is a Java and SQL enthusiast developer. He created the Data Geekery GmbH. He is the creator of jOOQ, a comprehensive SQL library for Java, and he is blogging mostly about these three topics: Java, SQL and jOOQ.

Java 8 Friday Goodies: The New New I/O APIs

At Data Geekery, we love Java. And as we’re really into jOOQ’s fluent API and query DSL, we’re absolutely thrilled about what Java 8 will bring to our ecosystem. We have blogged a couple of times about some nice Java 8 goodies, and now we feel it’s time to start a new blog series, the…

Java 8 Friday

Every Friday, we’re showing you a couple of nice new tutorial-style Java 8 features, which take advantage of lambda expressions, extension methods, and other great stuff. You’ll find the source code on GitHub.

Java 8 Goodie: The New New I/O APIs

In a previous blog post from this series, we have shown how Java 8′s lambda expressions improve on the existing (yet outdated) JDK 1.2 I/O API, mainly by helping you express java.io.FileFilter instances as lambda expressions.

Many readers have rightfully pointed out that much of the java.io API has been superseded by Java 7′s java.nio API, where “N” stands for “New” (I know. New. Old. Old-2. Old-2-FIXME. Old-2-TODO…). But things get even better with Java 8. We’re calling it the New New I/O APIs (NNIO), though jOOQ community members have suggested to call it “Enterprise IO”:

@lukaseder @brunoborges @ponzao +1 for “Enterprise IO” (in this case not expected to work of course) — Franz van Betteraey (@FrVaBe) January 15, 2014

Back to more constructive blogging. Let’s have a short walk (pun intended, see Files.walk()) around the improved Java 8 NIO features. Let’s first have a look at the new methods in java.nio.Files. It’s actually quite awesome that we can finally just list contents of a Path! In Java 8 we would use the newly introduced Files.list(), which returns a lazy Stream of files:

Files.list(new File(".").toPath())
     .forEach(System.out::println);

The output I get is this:

.\.git
.\.gitignore
.\.idea
.\java8-goodies.iml
.\LICENSE.txt
.\pom.xml
.\README.txt
.\src
.\target

Remember that forEach() is a “terminal method”, i.e. a method that consumes the stream. You mustn’t call any further methods on such a Stream.

We could also skip all hidden files and list only the first three “regular” files like this:

Files.list(new File(".").toPath())
     .filter(p -> !p.getFileName()
                    .toString().startsWith("."))
     .limit(3)
     .forEach(System.out::println);

The new output I get is this one:

.\java8-goodies.iml
.\LICENSE.txt
.\pom.xml

Now, that’s already pretty awesome. Can it get better? Yes it can. You can also “walk” a whole file hierarchy by descending into directories using the new Files.walk() method. Here’s how:

Files.walk(new File(".").toPath())
     .filter(p -> !p.getFileName()
                    .toString().startsWith("."))
     .forEach(System.out::println);

Unfortunately, the above will create a Stream of Paths excluding all the hidden files and directories, but their descendants are still listed. So we get:

Omitted:
.\.git

But listed:
.\.git\COMMIT_EDITMSG
.\.git\config
.\.git\description
[...]

It is easy to understand why this happens. Files.walk() returns a (lazy) Stream of all descendant files. The call to .filter() will remove the ones that are hidden from the Stream, but this has no influence on any recursive algorithm that might apply in the implementation of walk(). Frankly, this is a bit disappointing. We cannot leverage Java 7′s Files.walkFileTree() method, because the receiving FileVisitor type is not a @FunctionalInterface

We can, however, inefficiently work around this limitation with the following trivial logic:

Files.walk(new File(".").toPath())
     .filter(p -> !p.toString()
                    .contains(File.separator + "."))
     .forEach(System.out::println);

This now yields the expected

.
.\java8-goodies.iml
.\LICENSE.txt
.\pom.xml
.\README.txt
.\src
.\src\main
.\src\main\java
.\src\main\java\org
.\src\main\java\org\jooq
[...]

Good news, however, is the new Files.lines() method. The following example shows how we can easily read line by line from a file, trimming each line (removing indentation) and filtering out the empty ones:

Files.lines(new File("pom.xml").toPath())
     .map(s -> s.trim())
     .filter(s -> !s.isEmpty())
     .forEach(System.out::println);

The above yields:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.jooq</groupId>
<artifactId>java8-goodies</artifactId>
<version>1.0-SNAPSHOT</version>
[...]

Conclusion

Clearly, the notion of lazy evaluation will produce a big amount of confusion in the community, similar to the fact that a Stream can be consumed only once. We are placing a bet that the Java 8 Streams API will be the single biggest source of new Stack Overflow questions.

Nonetheless, the Streams API will be awesome, and next week on the Java 8 Friday series, we’ll see how we can leverage lambda expressions and Streams to sort things, before we’ll see how Java 8 will improve our database interactions!
 

Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

Leave a Reply


five × 9 =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books