Creating, merging and extending streams
Creating streams from collections, arrays, values and nullable values, merging with `concat`, extending with single elements, builders, laziness, closing and primitive streams.
On this page
- Picture it first: attach cars, then run the train
- Creating streams under the hood: pick the right source
- Merging streams: concat for two, flatMap for many
- Adding one element at the start or end
- Laziness, closing and parallelness of concat
- Primitive streams and the right kind of concat
- How to say it in the interview
🧱 What does it mean to create, merge or extend a stream?
Creating a stream means choosing the source that will feed a pipeline: a collection, array, fixed values, nullable value, builder, or empty source. Merging combines stream sources into one logical sequence, and extending adds a value before or after an existing stream. Think of it like joining train cars before departure: you decide which cars are attached, but passengers do not move through the train until the trip starts.
For this lesson, we'll focus on the practical Java 21 APIs: Collection.stream, Arrays.stream, Stream.of, Stream.empty, Stream.ofNullable, Stream.builder, Stream.concat, primitive concat, and the traps around arrays, closing, parallel streams, and deep concat chains. If you need zipping, pairing, or collection-level splitting, use Combining, flattening, zipping and splitting collections instead of forcing those ideas into concat.
Below the line (out of scope)
- How pipelines execute after creation: How a stream pipeline executes
filter,mapandflatMapas transformation operations: filter, map and flatMap- Infinite sources: Infinite streams with iterate and generate
- Zipping two sequences by position: Combining, flattening, zipping and splitting collections
The 30-second answer
Use collection.stream() for collections and Arrays.stream(array) for arrays, especially primitive arrays. Stream.of(a, b, c) is good for fixed values, Stream.empty() represents no values, and Stream.ofNullable(x) since Java 9 turns one possibly-null value into zero or one elements. For two streams, Stream.concat(left, right) is readable and lazy: it traverses the left stream, then the right stream, and closing the concatenated stream closes both inputs. For many streams, prefer Stream.of(s1, s2, s3).flatMap(Function.identity()) over deeply nested concat, because the Stream.concat javadoc warns that deep chains can create deep call stacks or even StackOverflowError. To add one element, concatenate Stream.of(value) before or after the existing stream. The result is parallel if either input is parallel, and primitive streams have their own IntStream.concat, LongStream.concat and DoubleStream.concat.
What you’ll learn
- Choose the right stream source: **
Collection.stream**,Arrays.stream,Stream.of,Stream.emptyandStream.ofNullable - Merge two streams with
Stream.concatand avoid deep concat chains for many streams - Add one element to the start or end with
concat(Stream.of(x), stream)orconcat(stream, Stream.of(x)) - Use
Stream.builderwhen values are discovered one by one before traversal - Explain concat laziness, close propagation and parallelness
- Use
IntStream.concatand primitive array streaming without theArrays.asList(int[])trap
01Picture it first: attach cars, then run the train
A stream source is not a bag of already-computed results. It is a way to obtain elements later. When you concatenate streams, you attach sources into a sequence: first car, second car, then maybe a small single-value car at the front or back.
The important part is nothing travels until a terminal operation starts traversal. That is why concat can be lazy and still preserve left-then-right encounter order.
A train diagram shows Stream.of adding one car before a left stream, Stream.concat joining left and right streams, and traversal happening only when the terminal operation pulls
Notice the shape. concat is not zip, merge-sort, or interleaving. It is append: drain the first stream, then drain the second.
02Creating streams under the hood: pick the right source
The most common source is a collection. If you already have a List<Order>, start there and let the collection provide its spliterator.
Try it yourself first
Choose the stream source for each value: a List<String>, a String[], an int[], a possibly-null User, and no users at all.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
final class StreamSourcesDemo {
record User(String name) { }
public static void main(String[] args) {
List<String> list = List.of("api", "db");
String[] names = {"Mina", "Omar"};
int[] scores = {10, 20, 30};
User maybeUser = null;
System.out.println(list.stream().count());
System.out.println(Arrays.stream(names).count());
System.out.println(Arrays.stream(scores).sum());
System.out.println(Stream.ofNullable(maybeUser).count());
System.out.println(Stream.<User>empty().count());
}
}
The output is:
2
2
60
0
0
Notice three things:
Collection.stream()is the normal collection path.Arrays.stream(int[])gives anIntStream, sosum()works without boxing.Stream.ofNullablehas existed since Java 9 and is a clean way to turn one nullable reference into zero or one elements.
The array trap is worth calling out because it appears in interviews and code reviews.
import java.util.Arrays;
final class PrimitiveArrayTrapDemo {
public static void main(String[] args) {
int[] scores = {10, 20, 30};
System.out.println(Arrays.asList(scores).size());
System.out.println(Arrays.stream(scores).count());
}
}
The output is:
1
3
Arrays.asList(scores) treats the whole int[] as one object because generics cannot use primitive element type int. Use Arrays.stream(scores) for primitive arrays. For Integer[], Arrays.asList behaves differently because Integer is a reference type.
Premium lesson
Keep reading with the Java Pass
The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.
- Merging streams: concat for two, flatMap for many
- Adding one element at the start or end
- Laziness, closing and parallelness of concat
- Primitive streams and the right kind of concat
- How to say it in the interview
From ₹199 · one-time payment · No auto-renewal