Back to Blog
Java

Java Stream.of: Creating Streams from Fixed Values

java stream of: Learn how to use Java Stream.of to create streams from fixed values, avoid the primitive array trap, and choose the right stream factory method.

Stream APIStream.ofJava collectionsFunctional programmingJava 8
Illustration of the Java Stream.of factory method converting individual values into a sequential stream pipeline.

The Stream.of() static factory method is the most direct way to create a Java stream of values without first building a collection. It accepts varargs, so you can pass individual elements directly at the call site:

Stream<String> names = Stream.of("ada", "grace", "linus");

The method returns a sequential stream that can be processed with the usual intermediate and terminal operations. Because it is a factory method rather than a constructor, it is the idiomatic entry point when the source data is a handful of known values rather than a collection or an array.

Basic Usage of Stream.of

Stream.of() is overloaded to accept either a single element or a varargs array. The single-element overload exists so that Stream.of(null) does not behave ambiguously; it returns a stream containing one null element. The varargs overload, by contrast, throws NullPointerException if the entire argument array is null.

Stream<String> single = Stream.of("java"); Stream<Integer> numbers = Stream.of(1, 2, 3, 5, 8);

Once the stream exists, you can chain operations in the usual way:

long count = Stream.of("red", "green", "blue") .filter(color -> color.length() > 3) .count();

The varargs signature means Stream.of() also accepts an array. This is where a common confusion arises.

The Array Type Trap

Passing a primitive array to Stream.of() does not produce a stream of the primitive's wrapper type. Because varargs treats the entire array as a single argument, Stream.of(new int[]{1, 2, 3}) yields a Stream<int[]> containing exactly one element: the array itself.

Stream<int[]> stream = Stream.of(new int[]{1, 2, 3});

If you want a stream of integers from a primitive array, use Arrays.stream(int[]) instead:

IntStream stream = Arrays.stream(new int[]{1, 2, 3});

For an object array, the behavior differs. Stream.of(new String[]{"a", "b"}) produces a Stream<String> with two elements, because the varargs expansion treats the object array as the variable-length argument list. This asymmetry between primitive arrays and object arrays is a frequent source of bugs.

Stream.of vs Arrays.stream vs Collection.stream

The choice between Stream.of(), Arrays.stream(), and Collection.stream() depends on the source data.

SourceRecommended methodResult
Fixed set of known valuesStream.of("a", "b")Stream<String>
Object arrayStream.of(array) or Arrays.stream(array)Stream<T>
Primitive arrayArrays.stream(intArray)IntStream
Existing collectioncollection.stream()Stream<E>

Stream.of() is the clearest choice when the values are known at the call site. When you already hold a List or a Set, calling stream() on the collection is more natural and avoids the extra indirection. For primitive arrays, Arrays.stream() is the only way to get a specialized IntStream, LongStream, or DoubleStream without boxing.

Handling Null and Empty Streams

Stream.of() with no arguments is not valid because the varargs overload requires at least the array reference. To create an empty stream, use Stream.empty():

Stream<String> empty = Stream.empty();

The single-element overload handles a single null gracefully:

Stream<String> withNull = Stream.of(null); // one element: null

But passing a null array to the varargs overload throws:

String[] arr = null; Stream.of(arr); // NullPointerException

This distinction matters when a method receives an array that may be null from an external caller. Guard with an explicit null check before calling Stream.of().

Combining Stream.of with Other Operations

Stream.of() is often used to build a stream from a small set of constants and then combine it with another stream using concat:

Stream<String> defaults = Stream.of("alpha", "beta"); Stream<String> userValues = userList.stream(); Stream<String> merged = Stream.concat(defaults, userValues);

Another common pattern is using Stream.of() to iterate over enum values or configuration keys:

Stream.of(DayOfWeek.MONDAY, DayOfWeek.FRIDAY) .map(day -> day.getDisplayName(TextStyle.SHORT, Locale.ENGLISH)) .forEach(System.out::println);

Because Stream.of() returns a regular Stream<T>, all standard intermediate operations are available without restriction.

Performance and Runtime Considerations

Stream.of() itself performs no lazy evaluation; the stream is created immediately, but element processing remains lazy until a terminal operation is invoked. The runtime cost of creating the stream is minimal because the varargs array is already materialized at the call site.

The main performance consideration is boxing. Stream.of(1, 2, 3) boxes each int into an Integer. For small, fixed sets this is irrelevant. For large primitive arrays, prefer Arrays.stream(int[]) to obtain an IntStream and avoid boxing overhead during map, filter, and reduce operations.

There is no meaningful difference in allocation cost between Stream.of(array) and Arrays.stream(array) for object arrays; both wrap the same underlying array. The stream does not copy the array, so modifications to the array after stream creation are visible to the stream if it has not yet been consumed.

When Stream.of Is the Right Choice

Use Stream.of() when the elements are known at the call site and the count is small or fixed. It reads naturally and avoids the ceremony of creating a temporary list. Avoid it when the source is already a collection, when you need a specialized primitive stream from an array, or when the array may be null and you need to distinguish that case from an empty stream.

The method is also a good fit for test fixtures where a stream of sample values is needed quickly:

Stream.of("user1", "user2", "user3") .map(User::new) .collect(Collectors.toList());

For production code that receives data from external sources, prefer explicit collection handling so that nullability and empty-state semantics are visible at the call site.

java stream of: Practical Usage and Code Examples | RYUSLOG DEV