Tuesday, September 7, 2021

3 Ways to Convert Java 8 Stream to an Array - Lambda Expression and Constructor Reference Example

One of the frequently asked Java 8 interview questions is, how you do convert a Java 8 Stream to an array? For example, you have a Stream of Strings and you want an array of String so that you can pass this to a legacy method that expects an array, how do you do that? Well, the obvious place to search is the Javadoc of java.util.stream.Stream class and there you will find a toArray() method. Though this method will convert the Stream to an array it has a problem, it returns an Object array. What will you do, if you need a String array? Well, you can use the overloaded version of the toArray(IntFunction generator), which expects a generator function to create an array of the specified type.

You can pass a lambda expression or constructor reference to this method to specify the type of array you want. This will return you an array of T i.e. if String contains String then it will return String array.

For example, streamOfString.toArray(String[]::new) will convert a Stream of String to an array of String and streamOfInts.toArray(int[]::new) will return an int[] from IntStream.

Now, let's see some code examples of converting a Java 8 Stream to an array. As I told you, you can convert the Stream into an object array using the toArray() method and an array of type T using overloaded Stream.toArray(IntFunction[] generator), we'll see examples of each of them to understand better.

And,  If you are not familiar with Stream behavior, I suggest you check to Learn Java Functional Programming with Lambdas & Streams by Rang Rao Karnam on Udemy, which explains Stream fundamentals in good detail.



Stream to an object array

This is quite straightforward, no trick about this one. You can simply call the toArray() method on Stream and it will give you an object array which contains all elements of the corresponding stream, as shown in the following example:

Stream<String> loans = Stream.of("Car Loan", "Home Loan", "Personal Loan");
Object[] objectArray = loans.toArray();
System.out.println(Arrays.toString(objectArray));

Output
[Car Loan, Home Loan, Personal Loan]

The only problem with this method is that it returns an object array that your client may not want because even if the object array contains String, you cannot pass it to a method expecting String[] in Java. The array is not covariant in Java. See Effective Java for more details.



A stream of T to an array of T via Stream.toArray() and lambda expression

If you want an array of type T from Stream of T i.e. a String array from Stream of String then you need to use the overloaded version of the toArray() method which expects a function which takes an integer and returns an array of the specified type. You can pass a lambda expression to this method to create an array of T as shown below:

Stream powerOfTen = Stream.of(1, 10, 100, 1000, 10000);
Integer[] array = powerOfTen.toArray(size -> new Integer[size]);
System.out.println(Arrays.toString(array));

Output
[1, 10, 100, 1000, 10000]

You can see that how easily the Stream of Integers is converted into Integer array. The lambda expression size -> new Integer[size] returns an array of Integer by expecting size, which is an int parameter.

Suppose, you want to convert a Stream of Integer to int[] and not Integer[] then you can use the mapToInt() function as shown below:

int[] intArray = powerOfTen.mapToInt(x -> x).toArray();
System.out.println(Arrays.toString(intArray));

Output
[1, 10, 100, 1000, 10000]

The Stream.mapToInt() function return an IntStream by converting all elements of Stream to int values. The java.util.strea.IntStream is a specialized Stream for int primitive values, hence it's toArray() method also return an int[] instead of Object array.

Suppose, you have a Stream of String which is numbers e.g. "1", "2", or "3" then also you can use mapToInt() to first convert String to int and then into an int array as shown below:

Stream<String> numbers = Stream.of("1", "2", "3", "4", "5");
int[] ints = numbers.mapToInt(Integer::parseInt).toArray();

The Integer::parseInt() is equal to lambda expression String str -> Integer.parseInt(str), hence used to convert each number String to the int value.

3 Ways to Convert Java 8 Stream to an Array



Java 8 Stream to Array via - method reference 

This is by far the best way to convert a Java 8 Stream to an Array. It's also the shortest and easiest way to do the job. Suppose you have a Stream of T and you want an array of T, you can use the toArray() method of Stream class with constructor reference as shown below:

T[] arrayOfT = streamOfT.toArray(T[]::new)

The T[]::new is an array reference in Java 8. It's like a method reference that accepts an int value and returns an array of the specified type. It is equal to lambda expression int i -> new T[i].

Let's convert a Stream of String object into a String array using this technique, here is the example:

Stream<String> cities = Stream.of("London", "Paris", "Tokyo");
String[] arrayOfCities = cities.toArray(String[]::new);
System.out.println(Arrays.toString(arrayOfCities));


This constructor reference solves a major limitation of Java. If you remember, it's not possible to create a generic array in Java e.g. T[] = new T[] will give a compile-time error. By using this trick, you can overcome that limitation and return a typed array from Stream instead of Object[].

If you want to learn more about this limitation and how constructor reference helps to get around them, I suggest reading Java SE 8 for Really Impatient by Cay S. Horstmann to learn more about that.

How to Convert Java 8 Stream to an Array using Lambda Expression



Stream to Array in Java 8 using Collector

This is the 3rd way to convert a Java 8 Stream to array. The idea is simple, you first convert Stream to ArrayList using any methods given here and then convert that ArrayList to an array using any methods given here. This is not exactly a Java 8 way to do the job but it allows you to use your existing knowledge of ArrayList to array conversion in Java 8.

Stream numbers = Stream.of(11, 22, 33, 44, 55); 
ArrayList list = numbers.collect(Collectors.toCollection(ArrayList::new));
Integer[] iArray = list.toArray(new Integer[list.size()]);


The collect() method is used to accumulate all elements of Stream into a Collection e.g. a list. Though there is a method called Collectors.toList() which converts Stream to List, it doesn't provide any guarantee about the type of list returned by this method.

Instead, by using Collectors.toCollection() you can convert it into any specific Collection like ArrayList. This is particularly useful if you want to preserve the order of elements while converting Stream to array in Java 8. You can read Java 8 in  Action to learn more about Collectors.

Best way to Convert Java 8 Stream to an Array using Constructor Reference



Java Program to convert Stream to an array - Example

Here is our complete Java example to convert a Stram to an array in Java 8. In this example, you will learn, how to convert the stream to object array, how to convert a stream of T to an array of T using lambda expression and constructor reference in Java, and finally, stream to array conversion using Collectors and ArrayList. You can choose whichever way you like, but the constructor reference example i.e. toArray(String[]::new) is the simplest, easiest, and the best way to convert a Stream to array in Java.



import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Java Program to convert a Java 8 Stream to Array
 */
public class Java8Demo {

    public static void main(String[] args) {

       // stream to object array in Java
        Stream<String> currencies = Stream.of("INR",
                                   "USD", "GBP", "EUR", "JPY");
        Object[] objectArray = currencies.toArray();
        System.out.println("Stream to object array in Java:");
        System.out.println(Arrays.toString(objectArray));

        // via - Stream.toArray() and lambda expression
        Integer[] primes = {2, 3, 5, 7, 11};

        List listOfInts = new ArrayList<>(Arrays.asList(primes));
        Integer[] array = listOfInts.stream()
                                    .toArray(size -> new Integer[size]);
        System.out.println("Stream to Integer array
                          using lambda expression in Java:");
        System.out.println(Arrays.toString(array));

        // via - method reference 
        array = listOfInts.stream()
                          .toArray(Integer[]::new);
        System.out.println("Stream to Integer array using
                              method reference in Java:");
        System.out.println(Arrays.toString(array));

        // via arraylist 
        ArrayList list = listOfInts.stream()
                               .collect(Collectors.toCollection(ArrayList::new));
        Integer[] iArray = list.toArray(new Integer[list.size()]);
        System.out.println("Stream to Integer array via ArrayList in Java:");
        System.out.println(list);
    }

}

}

Output
Stream to object array in Java:
[INR, USD, GBP, EUR, JPY]
Stream to Integer array using a lambda expression in Java:
[2, 3, 5, 7, 11]
Stream to Integer array using method reference in Java:
[2, 3, 5, 7, 11]
Stream to Integer array via ArrayList in Java:
[2, 3, 5, 7, 11]


Important points about Stream to array conversion

1. The java.util.stream.Stream class provides toArray() method to convert a Stream to array in Java. This method is overloaded to return an object array and an array of type T.

2. You can use lambda expression or constructor reference to convert a Stream of T into an array of T e.g. toArray(x -> new int[x]) will convert Stream of int to int[]. Similarly, toArray(x -> String[x]) will convert Stream of String to String array.

3. The best way to convert a Stream to an array in Java 8 is by using constructor reference i.e. toArray(int[]::new), which will convert Stream to an int array. The int[]::new is a constructor reference and it is similar to a method that expects an integer and returns an int[]. It is equivalent to x -> int[x] lambda expression, but it's slightly easier to read and write.


4. You can also convert Stream to an array by first converting it into an ArrayList. For that, you can use the collect() method to accumulate stream elements into a Collection e.g. ArrayList. The Collectors.toList() method will return a list of Stream elements but type of list is not guaranteed. If you want an ArrayList, just use Collectors.toCollection() method with constructor reference e.g. Collectors.toCollection(ArrayList::new))

5. You can use same ways to convert a parallel stream to array as well.

6. The Stream.toArray() method performs a terminal operator on Stream, hence you cannot reuse the Stream after calling this method. Any attempt to reuse Stream after calling toArray() will throw the following error:

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed

7. If you a Stream of Integer but you want to convert them into int[] and not Integer[], then you can use the mapToInt() function to convert Integer to int before converting Stream to array in Java as shown below:

streamOfInteger.mapToInt(x -> x).toArray();

The mapToInt() function return an IntStream, a specialized stream for primitive int type, the toArray() method of IntStream return int[] instead of Object[].


That's all about how to convert a Java 8 Stream to an array in Java. You have learned 3 different ways to achieve this task, first by using toArray() method and lambda expression and then by using constructor reference. The last way is not necessarily a Java 8 way because it first converts Stream to ArrayList and then to array, but it still does the job.


The best way to convert Stream to array in Java is by using toArray() and constructor reference i.e. toArray(T[]::new), it is both concise and clear. It's slightly less readable for first-timers but once you know that T[]::new create an array of T and expect an integer as size, it's much easier to write and read. 

Constructor reference can really make your code looks good, if you want some more examples, I suggest reading Java SE 8 for Really Impatient by Cay S. Horstmann, one of the best books to learn Java 8.


Other Java 8 tutorials you may like to explore
  • Best books to learn Java 8 (resource)
  • 10 Example of Joining String in Java 8 (see here)
  • 10 Example of forEach() method in Java 8 (example)
  • 20 Example of LocalDate and LocalTime in Java 8 (see here)
  • Difference between map() and flatMap() in Java 8 (answer)
  • 5 Books to Learn Java 8 and Functional Programming (list)
  • How to use Stream.flatMap in Java 8(example)
  • How to use Stream.map() in Java 8 (example)
  • How to use filter() in Collections with predicate and streams (tutorial)
  • 10 Example of Stream API in Java 8 (see here)
  • How to convert java.util.Date to java.time.LocalDate in Java 8? (tutorial)

Thanks a lot for reading this tutorial, if you have any questions or doubts, just write them in a comment, and if you like this post then please share with your friends and colleagues. It matters a lot. 

1 comment:

  1. I was able to find good advice from your blog posts.

    ReplyDelete