Back to Stream Creation & Utilities
Q5Question 5 of 18

Stream.toArray() vs toArray(IntFunction): Object[] vs typed array

Key Concepts
  • toArray() without arguments returns Object[] because generics type erasure prevents creating T[] at runtime
  • toArray(String[]::new) passes a generator that creates a correctly-typed array, returning String[]
  • The generator is an IntFunction that receives the array size and returns a new typed array
  • Never cast the no-arg toArray() result to a typed array: it throws ClassCastException
  • String[]::new is shorthand for size -> new String[size]
  • For primitive streams (IntStream), toArray() returns int[] directly without a generator
  • Always use the generator form when you need a typed array from an object stream

Definition

toArray() returns Object[] due to type erasure; toArray(String[]::new) passes a generator function that creates a correctly-typed array.
  • toArray() – returns Object[]; the element type is erased due to generics.
  • toArray(IntFunction) – the generator receives the array size and creates a typed array: String[]::new is size -> new String[size].
  • Why needed – Java cannot create new T[] at runtime due to type erasure, so you must provide the array constructor.
  • Primitive streams – IntStream.toArray() returns int[] directly because primitive types are not erased.

toArray() without arguments is like the bank giving you a generic bag of items. toArray(String[]::new) is like the bank giving you a properly labeled filing cabinet with the right-sized drawers. You tell the bank the drawer type (generator), and it fills them correctly.

Understanding the Concepts

How do the toArray overloads work?

JAVA
import java.util.stream.Stream;

// No-arg: returns Object[]
Object[] objects = Stream.of("SAV2001", "CUR3001", "FD4001").toArray();
// objects is Object[], not String[]!
// (String[]) objects → ClassCastException!

// With generator: returns String[]
String[] accounts = Stream.of("SAV2001", "CUR3001", "FD4001")
    .toArray(String[]::new);
// accounts is String[]

Why does casting fail?

JAVA
// WRONG: runtime array component type is Object
Object[] obj = Stream.of("SAV2001", "CUR3001").toArray();
String[] strings = (String[]) obj;  // ClassCastException!
// The array was created as new Object[2], not new String[2].

// CORRECT: generator creates String[]
String[] strings = Stream.of("SAV2001", "CUR3001").toArray(String[]::new);

Java cannot create new T[] at runtime due to type erasure. The generator provides the array constructor.

How does the generator work internally?

JAVA
// String[]::new is shorthand for:
// (int size) -> new String[size]

// The stream:
// 1. Counts elements
// 2. Calls generator(count) to create the array
// 3. Fills the array with elements
// 4. Returns the typed array

String[] ids = trades.stream()
    .map(Trade::getId)
    .toArray(String[]::new);  // generator receives the size, creates String[size]

How does it work for primitive streams?

JAVA
import java.util.stream.IntStream;

int[] numbers = IntStream.rangeClosed(1, 5).toArray();
// [1, 2, 3, 4, 5] – no generator needed for primitives!

long[] longs = java.util.stream.LongStream.of(100L, 200L, 300L).toArray();
double[] doubles = java.util.stream.DoubleStream.of(1.5, 2.5, 3.5).toArray();

Primitive types are not subject to type erasure, so the runtime knows the array component type.

How do you use toArray with custom types?

JAVA
package com.bank.service;

import java.math.BigDecimal;
import java.util.List;

public class ToArrayDemo {
    record Account(String id, BigDecimal balance) {}

    public static void main(String[] args) {
        List<Account> accounts = List.of(
            new Account("SAV2001", new BigDecimal("125000")),
            new Account("CUR3001", new BigDecimal("250000")));

        // Custom type array
        Account[] arr = accounts.stream().toArray(Account[]::new);
        System.out.println(arr.length); // 2
        System.out.println(arr[0].id()); // SAV2001

        // BigDecimal array from mapped stream
        BigDecimal[] balances = accounts.stream()
            .map(Account::balance)
            .toArray(BigDecimal[]::new);
    }
}

What is the comparison of collection methods?

| Method | Returns | Mutable | Type-safe |
|--------|---------|:---:|:---:|
| toList() (Java 16) | Unmodifiable List | No | Yes |
| collect(toList()) | Mutable List | Yes | Yes |
| toArray() | Object[] | Yes | No |
| toArray(T[]::new) | T[] | Yes | Yes |

What are common mistakes?

JAVA
// WRONG: casting no-arg toArray result
String[] bad = (String[]) Stream.of("A", "B").toArray(); // ClassCastException!

// CORRECT: use generator
String[] good = Stream.of("A", "B").toArray(String[]::new);

// WRONG: using old-style pre-sized array
// Stream has no toArray(T[]) like Collection does
// Stream.toArray only accepts IntFunction<T[]>

// CORRECT: pass constructor reference
String[] result = stream.toArray(String[]::new);

What should a senior engineer know?

  • The generator String[]::new is equivalent to (int size) -> new String[size].
  • For custom types: Trade[]::new creates a Trade array.
  • toArray is a terminal operation that consumes the stream.
  • Prefer toList() (Java 16) or collect(toList()) over toArray for most use cases. Use toArray when an API requires an array.
  • IntStream.toArray() returns int[] without a generator because primitive types are not erased.

Visual Overview

DIAGRAM
  toArray() vs toArray(generator):

  toArray():
  Stream<String> → Object[]  (type info lost)
  Cannot cast to String[] → ClassCastException

  toArray(String[]::new):
  Stream<String> → generator(3) → new String[3]
                → fill ["SAV", "CUR", "FD"]
                → String[] (correctly typed)

  IntStream.toArray():
  IntStream → int[] (no erasure for primitives)

Key Takeaway

toArray() without arguments returns Object[] because generics type erasure prevents creating T[] at runtime. The array's runtime component type is Object, so casting it to String[] throws ClassCastException. Always use toArray(String[]::new) to get a correctly-typed array.

The generator String[]::new is an IntFunction that receives the required array size and creates a new String array of that size. The stream fills it with elements. For primitive streams, toArray() returns int[], long[], or double[] directly because primitive types are not subject to erasure.

Remember one sentence: always use toArray(Type[]::new) for typed arrays because the no-arg toArray() returns Object[] due to type erasure.

---

Share
Use ← → arrow keys to navigate