Core Java

From Arrays to Streams and Back with Java 8

Not long ago we upgraded some Eclipse plug-in projects to Java 8. And never looked back since. Among many other things, filtering, mapping, and finding elements in collections has become so much easier and more concise with lambdas and the streams API. Nothing new so far for the most of you, I guess.

But many existing APIs use arrays in arguments and/or return arrays. For an example, consider this fictional but nontheless common method signature:

String[] filterStrings( String... input );

And with it comes the extra effort of obtaining a stream from an array in order to be able to elegantly filter, map, reduce, etc. the elements. And then getting back an array that can be passed on to the old school APIs.

Forth…

To obtain a stream from an array there are plenty of choices. For example, this line of code

Stream stream = Stream.of( "a", "b", "c" );

produces a stream with the specified elements. The same can also be achieved through:

Stream stream = Arrays.stream( "a", "b", "c" );

In fact, Stream.of() uses Arrays.stream() to accomplish the task. Making the detour via a List also results in a stream:

Stream stream = Arrays.asList( "a", "b", "c" ).stream();

… and Back

Once we have a stream all stream features are available, for example to filter empty strings from an array of Strings:

Stream.of( "a", "", "b", "", "c", "" ).filter( string -> !string.isEmpty() );

But how to get back an array with the result?

There are collectors for sets and lists and whatnot, but not for simple arrays. This code snippet

List<String> list
  = Stream.of( ... ).filter( ... ).collect( Collectors.toList() );
String[] array = list.toArray( new String[ list.size() ] );

uses toList() to obtain a list of the filtered input and then turns the list into an array in a second step.

I was almost about to implement a custom array collector to eliminate the extra step. Until I discovered that there is a terminal operation to capture the result of a stream into an array as simple as that:

String[] array = Stream.of( ... ).toArray( size -> new String[ size ] );

toArray() requires a generator, a reference to a method that is able to create an array of the requested size. Here an array of type String is created.

But wait, there is an even simpler way. As mentioned above, the generator is a function that can create an array of a requested size. And the makers of Java 8 were so kind to introduce some syntactic sugar to directly reference an array constructor.

By adding an opening and closing square bracket to a constructor reference, an array constructor reference can be expressed, e.g. Type[]::new.. Hence the above line can be rewritten like so:

String[] array = Stream.of( ... ).toArray( String[]::new );

The String[]::new expression is expanded to size -> new String[ size ] by the compiler. And therefore the generated byte code is the same as with the previous approach but I find the latter much more concise.

And moreover, it eliminates the admittedly unlikely but still possible error of getting the size of the generated array wrong. Consider this:

String[] array = Stream.of( "a", "b", "c" ).toArray( size -> new String[ 1 ] );

The created array is obviously too small. Its actual size (one) will never be able to hold the three resulting elements. And thus will end up in an IllegalStateException. When using the array constructor reference the compiler will ensure to create an appropriately sized array.

Of course, there is also a generic toArray() method that returns an array of Objects and can be used if the actual type of the resulting array doesn’t matter.

Concluding from Arrays to Streams and Back

Like my dear colleague Ralf, many programmers prefer collections over arrays in API interfaces. But there are still many ‘old-fashioned’ APIs that require you to deal with arrays. And as it is with APIs, those won’t go away soon.

But whichever way you prefer, or whichever way you are forced to go through existing code, I found it good news that Java 8 provides a decent bridge between the two worlds.

If you have questions, suggestions or would like to share your experiences in this area please leave a comment.

Reference: From Arrays to Streams and Back with Java 8 from our JCG partner Rudiger Herrmann at the Code Affine blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Dean
Dean
9 years ago

Elegant, thanks!

Back to top button