Java Stream toArray(): Convert a Stream to an Array

Java Stream toArray() converts a stream to an array. Learn the String[]::new generator, int[] from primitive streams, ArrayStoreException and toList().

Stream.toArray with a String[]::new generator receiving the element count and returning a typed array

In Java, the Stream.toArray() method is a terminal operation that collects the elements of a stream into an array, either an Object[] or, with a generator such as String[]::new, an array of the exact type we need. Primitive streams such as IntStream have their own toArray() that returns an int[].

We convert a stream to an array when an API expects an array, such as a varargs method, ProcessBuilder or a table model, or when a fixed-size, index-based structure fits the next step better than a list.

The following example shows the three forms of toArray() on small streams.

Object[] objects = Stream.of("apple", "banana").toArray();                    // [apple, banana]
String[] fruits = Stream.of("apple", "banana").toArray(String[]::new);         // [apple, banana]
int[] scores = Stream.of(7, 9, 4).mapToInt(Integer::intValue).toArray();      // [7, 9, 4]

Only the generator form returns a String[], whereas the no-argument form returns an Object[] even for a stream of strings. Further down, we explain why the generator is needed, typed and primitive arrays, the exceptions toArray() can throw, the order of elements and how toArray() compares with toList().

1. toArray() and toArray(IntFunction)

The Stream interface declares two toArray() methods. The no-argument version returns an Object[], because generics are erased at runtime and the stream does not know that its elements are strings. The second version takes an IntFunction<A[]>, called the generator, which receives the number of elements and returns a new array of that length.

MethodReturnsTypical call
Object[] toArray()An Object[] with all elementsstream.toArray()
<A> A[] toArray(IntFunction<A[]> generator)An array created by the generatorstream.toArray(String[]::new)
Stream.toArray with a String[]::new generator receiving the element count and returning a typed array
The generator receives the element count and creates the array, so the result has the exact runtime type we asked for

The constructor reference String[]::new is a short form of the lambda size -> new String[size], as explained in method references. Both forms do the same thing, and the constructor reference is the one most code uses.

String[] byReference = Stream.of("kiwi", "fig").toArray(String[]::new);          // [kiwi, fig]
String[] byLambda = Stream.of("kiwi", "fig").toArray(size -> new String[size]);   // [kiwi, fig]

A cast does not turn an Object[] into a String[]. The array object itself has the runtime type Object[], so the cast compiles and fails at runtime.

String[] wrong = (String[]) Stream.of("kiwi").toArray();     // ClassCastException

2. Converting a Stream to a String Array or a Record Array

In most code, toArray() is the last step of a pipeline that filters and maps the elements. For example, we keep the fruits that have more than four letters and convert them to upper case before we create the array.

List<String> basket = List.of("kiwi", "apple", "fig", "banana");

String[] longNames = basket.stream()
        .filter(name -> name.length() > 4)
        .map(String::toUpperCase)
        .toArray(String[]::new);                    // [APPLE, BANANA]

The generator works the same way for our own types. With a record, the array constructor reference is Product[]::new.

record Product(String name, int price) {}
List<Product> catalog = List.of(new Product("apple", 5), new Product("melon", 12), new Product("fig", 4));
Product[] cheap = catalog.stream().filter(p -> p.price() < 10).toArray(Product[]::new);   // [Product[name=apple, price=5], Product[name=fig, price=4]]

Arrays of arrays work too, because String[][]::new is a valid generator. We use it in section 6 to turn text lines into a table.

3. Getting int[], long[] and double[] From a Stream

A Stream<Integer> can produce an Integer[], but not an int[], because int[]::new does not match IntFunction<A[]> with an object type A. To get a primitive array, we switch to a primitive stream with mapToInt(), mapToLong() or mapToDouble(), and call its toArray().

List<Integer> ratings = List.of(4, 5, 3);

Integer[] boxed = ratings.stream().toArray(Integer[]::new);                  // [4, 5, 3]
int[] primitive = ratings.stream().mapToInt(Integer::intValue).toArray();    // [4, 5, 3]
double[] prices = Stream.of("1.5", "2.25").mapToDouble(Double::parseDouble).toArray();   // [1.5, 2.25]

In the other direction, an IntStream produces an Integer[] after boxed(). Ranges and limited infinite streams convert the same way, which is handy for test data.

int[] firstTen = IntStream.iterate(1, i -> i + 1).limit(10).toArray();           // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Integer[] evens = IntStream.rangeClosed(1, 6).filter(i -> i % 2 == 0).boxed().toArray(Integer[]::new);   // [2, 4, 6]

For more on primitive conversions, see boxed streams and converting between primitive and object arrays.

4. ArrayStoreException and Wrong Generators

The generator must create an array whose component type can hold every element, otherwise toArray() throws ArrayStoreException at runtime. The compiler does not catch it, because the generator’s type parameter A is not tied to the stream’s element type.

Stream<Object> mixed = Stream.of("apple", 42);
String[] notAllStrings = mixed.toArray(String[]::new);       // ArrayStoreException: java.lang.Integer

The safe version keeps only the elements of the target type before collecting. A filter() with String.class::isInstance followed by a map() with String.class::cast does it in two short steps.

List<Object> values = List.of("apple", 42, "fig");
String[] onlyStrings = values.stream().filter(String.class::isInstance).map(String.class::cast).toArray(String[]::new);   // [apple, fig]

The generator must also return an array of the requested size, no larger and no smaller. For a stream with a known size, a different length throws IllegalStateException. Writing String[]::new avoids the problem, because it always uses the size it receives.

String[] tooSmall = Stream.of("a", "b", "c").toArray(n -> new String[2]);   // IllegalStateException: Begin size 3 is not equal to fixed size 2

5. toArray() vs toList() vs Collection.toArray()

A stream can end in an array or a list, and a list can also become an array without a stream. The choice depends on what the next piece of code expects.

CodeResultNotes
stream.toArray(String[]::new)String[]Fixed size, elements can be replaced
stream.toList()Unmodifiable List (Java 16)Allows null elements, see stream to list
list.toArray(String[]::new)String[] (Java 11)No stream needed when there is no filtering or mapping
list.toArray(new String[0])String[]The older form, still common in existing code

When the data is already in a collection and needs no pipeline step, Collection.toArray(IntFunction) is the shortest choice, and it uses the same generator syntax. More conversions are listed in converting between a list and an array.

List<String> tags = List.of("new", "sale");
String[] tagArray = tags.toArray(String[]::new);                              // [new, sale]

The order of the array follows the encounter order of the stream, also for parallel streams. A list or a Stream.of() source gives the same order every time, whereas a HashSet source has no defined order to begin with.

Integer[] doubled = Stream.of(5, 3, 8).parallel().map(i -> i * 2).toArray(Integer[]::new);   // [10, 6, 16]

6. Building a String[][] From CSV Lines

A small admin tool shows an uploaded CSV file in a Swing JTable, whose constructor takes an Object[][] for the rows. We read the lines with String.lines(), skip blank lines, split each line into its cells and collect the rows with String[][]::new.

String upload = """
        apple,5,red
        banana,3,yellow

        kiwi,8,green
        """;

String[][] rows = upload.lines()
        .filter(line -> !line.isBlank())
        .map(line -> line.split(","))
        .toArray(String[][]::new);
int rowCount = rows.length;                         // 3
String kiwiColor = rows[2][2];                      // "green"

The same pipeline feeds any API that wants a two-dimensional array. For quoted cells that contain commas, a CSV parser is the safer choice, as described in parsing CSV files in Java.

7. Stream to Array FAQs

Typed arrays, primitive arrays, reuse and null entries come up in the related searches for stream-to-array conversion.

7.1. Why does toArray() return Object[] instead of String[]?

Generics are erased at runtime, so the stream cannot create a String[] without help. Pass String[]::new as the generator to get a typed array.

7.2. How do we convert a Stream<Integer> to int[]?

Map it to an IntStream first and call toArray(), as in section 3.

int[] values = Stream.of(1, 2, 3).mapToInt(Integer::intValue).toArray();   // [1, 2, 3]

7.3. Can we call toArray() twice on the same stream?

No. The toArray() method is a terminal operation, so the second call throws IllegalStateException. Keep the array and reuse it, or create a new stream from the source.

7.4. Does toArray() keep null elements?

Yes. A null element becomes a null entry in the array. Filter with Objects::nonNull before toArray() when the array must not contain null.

7.5. How do we convert an array back to a stream?

Use Arrays.stream(array) or Stream.of(array). For an int[], Arrays.stream() returns an IntStream, as covered in converting between stream and array.

8. Conclusion

The toArray() method ends a stream and returns an array. Without arguments, we get an Object[]; with a generator such as String[]::new, we get an array of the exact type, and primitive streams return int[], long[] or double[].

A generator with the wrong component type throws ArrayStoreException, and one that returns the wrong length can throw IllegalStateException, so the constructor reference form is the safe default. The array keeps the encounter order, and for data already in a list, list.toArray(String[]::new) skips the stream.

9. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.