Definition
- Collection.stream() uses the collection's spliterator, which reports SIZED and SUBSIZED characteristics.
- Arrays.stream(array) gives the same well-characterized spliterator for arrays.
- Good characteristics enable buffer pre-sizing and balanced parallel splitting.
- Stream.of(primitiveArray) creates a one-element stream of the whole array, not its elements.
- Stream.of is for a few fixed literals, not large datasets.
- Choosing the right factory is a performance and correctness decision.
At the National Bank of India, Priya uses the ledger's own reader, which knows how many pages there are and how to split them evenly among clerks. She does not use a generic feeder that does not know the page count or how to divide the work.
Understanding the Concepts
Why do spliterator characteristics matter?
When a stream is created, the source provides a spliterator that reports characteristics like SIZED (known element count) and SUBSIZED (sub-splits also have known sizes). The stream framework uses these to:
- Pre-size internal buffers (avoiding resizing during
toArrayorcollect). - Divide work evenly for parallel processing.
- Optimize terminal operations like
count()which can return the known size without traversing.
How do different factories compare?
| Factory | SIZED | SUBSIZED | Splits evenly | Best for |
|---|---|---|---|---|
| collection.stream() | Yes | Yes | Yes (ArrayList) | Collections of any size |
| Arrays.stream(array) | Yes | Yes | Yes | Arrays |
| Stream.of(a, b, c) | Yes | Yes | Yes | Few fixed literals |
| Stream.of(primitiveArray) | Yes (size 1) | N/A | N/A | Bug: not what you want |
| Stream.generate(supplier) | No | No | No | Infinite streams |
What is the primitive array gotcha with Stream.of?
package com.bank.core;
import java.util.Arrays;
import java.util.stream.Stream;
public class StreamSourceComparison {
public static void main(String[] args) {
int[] salaries = {50000, 60000, 70000, 80000, 90000};
// WRONG: Stream.of(int[]) creates a one-element Stream<int[]>
long wrongCount = Stream.of(salaries).count();
System.out.println("Stream.of(int[]): " + wrongCount); // 1
// CORRECT: Arrays.stream(int[]) creates an IntStream of elements
long correctCount = Arrays.stream(salaries).count();
System.out.println("Arrays.stream(int[]): " + correctCount); // 5
// CORRECT for reference arrays: Stream.of works
String[] names = {"Rajesh", "Priya", "Meera"};
long refCount = Stream.of(names).count();
System.out.println("Stream.of(String[]): " + refCount); // 3
}
}
Stream.of(int[]): 1
Arrays.stream(int[]): 5
Stream.of(String[]): 3
Why does Collection.stream() perform better for large data?
The collection's spliterator reports that it is sized and can split evenly (for ArrayList), so the framework:
- Knows the total count without traversing.
- Pre-allocates arrays of the right size for
toArray. - Divides work into balanced chunks for parallel streams.
Stream.of is implemented as a thin wrapper over an array spliterator when called with varargs, so it has good characteristics too. But its intended use is a few literal values, and using it with a single array argument can cause the primitive gotcha.
What are common mistakes?
Mistake: Using Stream.of for a primitive array and getting wrong results.
// WRONG: one-element stream containing the whole array
int[] amounts = {1000, 2000, 3000};
int sum = Stream.of(amounts)
.mapToInt(a -> a) // does not compile: a is int[], not int
.sum();
// CORRECT: Arrays.stream for primitive arrays
int sum = Arrays.stream(amounts).sum(); // 6000
What should a senior engineer know?
Good spliterator characteristics (SIZED, SUBSIZED, balanced splitting) are a real performance factor for large and parallel work. Choosing the correct source factory is not a style preference but a performance and correctness decision. Use Collection.stream() for collections, Arrays.stream for arrays, and Stream.of only for a few literal values.
What about LinkedList vs ArrayList for parallel?
LinkedList.stream() has a spliterator that reports SIZED but splits poorly because it must traverse to find the midpoint. ArrayList.stream() splits in O(1) by index. For parallel work, the data structure matters as much as the factory choice.
What are common interview follow-up questions?
- "What is the primitive array gotcha?"
Stream.of(int[])creates a one-element stream of the array, not a stream of its elements. - "How does ArrayList split vs LinkedList?" ArrayList splits in O(1) by index; LinkedList splits by traversal.
- "Does Stream.of have good characteristics?" Yes, but it is intended for small literal sets.
- "When should you use Arrays.stream?" For any existing array, especially primitive arrays.
Visual Overview
Collection.stream() Arrays.stream(arr)
┌──────────────────┐ ┌──────────────────┐
│ SIZED: yes │ │ SIZED: yes │
│ SUBSIZED: yes │ │ SUBSIZED: yes │
│ Splits evenly: ✓ │ │ Splits evenly: ✓ │
│ Best for large │ │ Best for arrays │
└──────────────────┘ └──────────────────┘
Stream.of(int[]) Stream.of("A","B")
┌──────────────────┐ ┌──────────────────┐
│ Creates Stream │ │ SIZED: yes │
│ <int[]> ← BUG │ │ For few literals │
│ count() = 1 │ │ count() = 2 │
└──────────────────┘ └──────────────────┘
Key Takeaway
For large datasets, prefer the source's own stream() method or Arrays.stream for arrays over Stream.of. The collection's stream and Arrays.stream provide spliterators with good characteristics: known size and balanced splitting, which the framework uses to pre-size buffers and divide parallel work evenly.
Stream.of is intended for a handful of fixed literal values and has a well-known gotcha: Stream.of(primitiveArray) creates a one-element stream containing the whole array rather than streaming its elements. For a primitive array, use Arrays.stream to get the correct element-by-element streaming.
At the National Bank of India, Rajesh uses the collection's own stream() for all real data processing and reserves Stream.of for test fixtures with a few literal values. Good spliterator characteristics are a real performance factor for large and parallel work, so choosing the correct source factory is a performance and correctness decision.
Remember one sentence: use Collection.stream() for collections and Arrays.stream for arrays because their spliterators report size and split evenly, while Stream.of is only for a few fixed literals.
---