Definition
- toArray(IntFunction) takes an array generator that creates the array of the needed size.
- Employee[]::new is an
IntFunctionsupplying the type and size. - No-argument toArray() returns
Object[], losing the element type. - Use case: when an API requires an array, for varargs, or for legacy interoperability.
- Primitive streams have their own
toArray()returningint[]orlong[]directly.
At the National Bank of India, Priya empties the conveyor belt into a correctly-labelled box that holds exactly employees, not a generic box labelled 'Object'. The label matters because the next system expects an Employee box.
Understanding the Concepts
Why do you need the typed toArray?
The no-argument stream.toArray() returns an Object[], which loses the element type. Most APIs that accept arrays expect a specific type like Employee[]. The typed toArray(Employee[]::new) creates a properly typed array.
How do you use it?
package com.bank.hr;
import java.math.BigDecimal;
import java.util.List;
public class ToArrayDemo {
record Employee(String id, String name, BigDecimal salary) {}
public static void main(String[] args) {
List<Employee> staff = List.of(
new Employee("SAV2001", "Rajesh", new BigDecimal("1800000")),
new Employee("SAV2002", "Priya", new BigDecimal("1200000")),
new Employee("SAV2003", "Meera", new BigDecimal("1500000"))
);
// Typed array
Employee[] array = staff.stream().toArray(Employee[]::new);
System.out.println("Type: " + array.getClass().getSimpleName());
System.out.println("Length: " + array.length);
// Untyped array (Object[])
Object[] objects = staff.stream().toArray();
System.out.println("Untyped: " + objects.getClass().getSimpleName());
}
}
Type: Employee[]
Length: 3
Untyped: Object[]
How does the generator function work?
Employee[]::new is equivalent to size -> new Employee[size]. The stream calls this function with the required array length and fills the returned array with elements.
// These are equivalent:
Employee[] a = staff.stream().toArray(Employee[]::new);
Employee[] b = staff.stream().toArray(size -> new Employee[size]);
When should you use toArray vs toList?
| Aspect | toArray(T[]::new) | collect(toList()) |
|---|---|---|
| Result type | Fixed-size array T[] | Flexible List |
| Generics | Limited (arrays and generics mix poorly) | Full support |
| Modification | Fixed size, elements replaceable | Resizable (ArrayList) |
| Use when | API requires array, varargs, primitives | Most application code |
What about primitive streams?
// IntStream.toArray() returns int[] directly, no boxing
int[] salaries = java.util.stream.IntStream.of(50000, 60000, 70000).toArray();
// Returns int[], not Integer[]
// LongStream.toArray() returns long[]
long[] ids = java.util.stream.LongStream.rangeClosed(1, 100).toArray();
What are common mistakes?
Mistake: Using the no-argument toArray() and trying to cast.
// WRONG: ClassCastException
Employee[] wrong = (Employee[]) staff.stream().toArray();
// Object[] cannot be cast to Employee[]
// CORRECT: use the typed generator
Employee[] correct = staff.stream().toArray(Employee[]::new);
What should a senior engineer know?
Default to List for flexibility and reserve toArray for specific needs: primitive performance via primitive-stream toArray, API/varargs interoperability, and fixed-size contracts. Converting a list to an array later is trivial with list.toArray(Employee[]::new) if needed.
What are common interview follow-up questions?
- "Why not use the no-argument toArray?" It returns
Object[], losing the element type. - "When should you use toArray?" When an API requires an array, for varargs, or for primitives.
- "How do you get int[] from a stream?" Use
IntStream.toArray(), notStream..toArray()
Visual Overview
staff.stream().toArray(Employee[]::new)
│
│ stream determines size = 3
│ calls Employee[]::new with size 3
▼
┌──────────────────────────────────┐
│ new Employee[3] │
│ [0] = Rajesh │
│ [1] = Priya │
│ [2] = Meera │
└──────────────────────────────────┘
Result: Employee[] (typed, not Object[])
Key Takeaway
To collect a stream into a typed array, use toArray(Employee[]::new). The constructor reference is an IntFunction that the stream calls with the required length to allocate a properly typed Employee[] array. Without it, the no-argument toArray() returns an Object[] that loses the element type and forces unsafe casting.
At the National Bank of India, Rajesh uses toArray only when a legacy reporting API requires an Employee[]. For most code, he defaults to collect(toList()) because a List is more flexible, supports generics cleanly, and integrates with the Collections framework.
For primitive values, the primitive stream's toArray() returns int[] or long[] directly without boxing or a generator. Converting a list to an array later is trivial with list.toArray(T[]::new), so starting with a list and converting only when needed is the practical default.
Remember one sentence: toArray(T[]::new) gives a typed array; default to toList() for flexibility and use toArray only when an API specifically requires an array.
---