What's the difference between Stream.builder() and calling stream() on an ArrayList in Java?

This is an implementation detail, but yes, the builder is better optimized to the use case of being incrementally filled, followed by an operation streaming over the contained elements.

In contrast, an ArrayList has to support arbitrary modification and random access.

So, when repeatedly adding elements to an ArrayList without specifying a correctly predicted initial capacity, it may need to allocate a new, larger array and copy the current array into it whenever the current capacity is exhausted.

In contrast, the builder has a special support for the single element case, which doesn’t need an array at all. Then, if more elements are added, it will turn to a spined buffer. This buffer starts with a small array like ArrayList but when its capacity is exhausted, it begins to use an array of arrays instead of repeatedly copying the array to a larger flat array.

So this saves the copying costs you’d have when filling an ArrayList. You can save these costs for ArrayList by specifying the right initial capacity, but that only works when an estimate is available. Specifying an initial capacity also removes the optimization for the empty case. So generally, the stream builder can deal with unknown sizes much better.

Another property of this design is that Stream.Builder can deal with more than 2³¹ elements, unlike ArrayList, if you have enough memory.


Stream.builder() is not a terminal operation, so it's lazy. Using the second one, in theory, uses more memory. From the Stream.Builder Javadoc, This allows the creation of a Stream by generating elements individually and adding them to the Builder (without the copying overhead that comes from using an ArrayList as a temporary buffer.)