Collecting to an Array – Streams
By Stephen Trude / September 23, 2023 / No Comments / Certifications of Oracle, Collecting to an Array, Consumer Action on Stream Elements
Collecting to an Array
The overloaded method toArray() can be used to collect or accumulate into an array. It is a special case of a mutable reduction, and as the name suggests, the mutable container is an array. The numeric stream interfaces also provide a counterpart to the toArray() method that returns an array of a numeric type.
Object[] toArray()
This terminal operation returns an array containing the elements of this stream. Note that the array returned is of type Object[].
<A> A[] toArray(IntFunction<A[]> generator)
This terminal operation returns an array containing the elements of this stream. The provided generator function is used to allocate the desired array. The type parameter A is the element type of the array that is returned. The size of the array (which is equal to the length of the stream) is passed to the generator function as an argument.
The zero-argument method toArray() returns an array of objects, Object[], as generic arrays cannot be created at runtime. The method needs to store all the elements before creating an array of the appropriate length. The query at (1) finds the titles of the CDs, and the toArray() method collects them into an array of objects, Object[].
Object[] objArray = CD.cdList.stream().map(CD::title)
.toArray(); // (1)
//[Java Jive, Java Jam, Lambda Dancing, Keep on Erasing, Hot Generics]
The toArray(IntFunction<A>) method accepts a generator function that creates an array of type A, (A[]), whose length is passed as a parameter by the method to the generator function. The array length is determined from the number of elements in the stream. The query at (2) also finds the CD titles, but the toArray() method collects them into an array of strings, String[]. The method reference defining the generator function is equivalent to the lambda expression (len -> new String[len]).
String[] cdTitles = CD.cdList.stream().map(CD::title)
.toArray(String[]::new); // (2)
//[Java Jive, Java Jam, Lambda Dancing, Keep on Erasing, Hot Generics]
Examples of numeric streams whose elements are collected into an array are shown at (3) and (4). The limit() intermediate operation at (3) converts the infinite stream into a finite one whose elements are collected into an int array.
int[] intArray1 = IntStream.iterate(1, i -> i + 1).limit(5).toArray();// (3)
// [1, 2, 3, 4, 5]
int[] intArray2 = IntStream.range(-5, 5).toArray(); // (4)
// [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]
Not surprisingly, when applied to infinite streams the operation results in a fatal OutOfMemoryError, as the method cannot determine the length of the array and keeps storing the stream elements, eventually running out of memory.
int[] intArray3 = IntStream.iterate(1, i -> i + 1) // (5)
.toArray(); // OutOfMemoryError!
If the sole purpose of using the toArray() operation in a pipeline is to convert the data source collection to an array, it is far better to use the overloaded Collection.toArray() methods. For one thing, the size of the array is easily determined from the size of the collection.
CD[] cdArray1 = CD.cdList.stream().toArray(CD[]::new); // (6) Preferred.
CD[] cdArray2 = CD.cdList.toArray(new CD[CD.cdList.size()]); // (7) Not efficient.
Like any other mutable reduction operation, the toArray() method does not terminate when applied to an infinite stream, unless it is converted into a finite stream as at (3) above.