Lambda expressions are one of the coolest features of Java 8 so far. But I think there is a (not so) secret weapon that shows just how powerful such “syntactic sugar” can be in increasing code readability and writeability. When you increase the expressiveness of code it unlocks a higher level of understanding that can make the most obtuse of tasks simple.
That secret weapon? The Java Streams API. Recently I entered an online contest simply to use the Streams in a more performance oriented environment. My surprise was that the API made development of the principal loop very simple, and very malleable to the many changes I would make. Here are 8 cool things I learned.
1. Streams don’t require lambda expressions
Even though this API really shines with Lambdas, you don’t have to use them. You could always fall back to anonymous classes, but why would you? More likely scenarios are using a method reference (such as
Integer::valueOf), or an instance object. Using a method reference allows you to put complex multiline logic out of the main stream loop, like when you are optimizing a hash set lookup away. The instance object allows for implementation of the Gang of Four Strategy Pattern. But please, don’t use anonymous classes unless you have to.
2. Peek into the stream to debug it.
At any point in your stream where you would add an intermediate operator, you can add one called
peek. This operation takes a consumer and expects no result, which is basically a lambda that returns void. My favorite use of peek is to send debugging information to the system, such as
.peek(System.out::println) .peek(it -> System.out.printf("it is %s%n", it)
3. Random stuff can be streamed
Streams are not limited to collections or arrays of stuff, or even fixed lists of stuff. If you can create an
Iterator or a
Supplier lambda that creates the values of the stream then you can create a stream with the methods on the
java.util.stream.StreamSupport class. Imagine a stream driven by constant measurements such as memory consumption or network throughput…
4. Random numbers can be streamed
If you are looking for plain old randomness, such as from a
java.util.Random there are three new sets of methods on that class to stream
doubles(). Overloaded versions of those methods let you set the bounds, seed, and total amount of random numbers streamed.
5. Streaming readers
Another common task for java developers is parsing a file line by line. Now we have a
lines() method on
java.io.BufferedReader that will turn that I/O stream in to a stream of strings, fit for stream processing.
6. Streaming file trees
If walking through the content of the files isn’t your thing, how about walking a file tree?
java.nio.file.Files has several methods that return Streams.
list() will list the files in one directory,
walk(...) will do it recursively, and
filter(...) will do it recursively with some attribute driven filtering (something that is convoluted when you just have a Path object). You can still get at the contents via streams with the
7. Streaming complex text
If you are still fixated on text processing, but the content isn’t line based, you can use the
splitAsStream(CharSequence) method on your
java.util.regex.Pattern instance. Useful for those million column CSV files, or your classpath.
8. Streaming zip files
Speaking of long classpath searches, you can also stream your
java.util.jar.JarFiles with the conveniently named
stream() method, returning a
JarEntry respectively. Nothing like using Java 8 streams to mine your classpath for JavaBeans.
If you’ve made it this far you surely realize that these aren’t the basic things you would do with streams. I figure there will be more than enough blog posts walking through the basics of streams. But I consider these to be some of the hidden gems that show the potential of streams. But mining your classpath for jar declared JavaBeans, using lambdas and streams? There’s an old/new juxtaposition worthy of a lunch hour hack!