Skip to Content
Course content

70: The Streams API: Creating Streams

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was reviewing a pull request from a developer who was building a reporting module for a logistics platform. He had written a method to extract a list of "high-priority" shipment IDs from a massive list of order objects. The code was a wall of boilerplate: he initialized a new ArrayList, started a for-each loop, added an if statement to check the priority, and then manually added the ID to the new list. It was 20 lines of code to do one simple thing. I remember telling him, "You're spending all your time managing the plumbing instead of the data." When I showed him how to simply call .stream() on his collection, the look of relief on his face was genuine. He finally stopped worrying about the how and started focusing on the what.

Turning Collections into Pipelines

The most common way you'll encounter streams is by deriving them from an existing collection. Since Collection is a super-interface for List and Set, almost any group of objects you're already working with can be converted into a stream immediately. You just call the .stream() method.

List<String> employees = List.of("Alice", "Bob", "Charlie", "Diana");
Stream<String> employeeStream = employees.stream();

It's important to realize that calling .stream() doesn't actually move the data or create a copy of the list. It just creates a "view" or a pipeline that allows you to process those elements sequentially. If you modify the original list while the stream is running, you're asking for a ConcurrentModificationException, so I'd suggest treating your source collection as read-only once the stream starts.

Handling Arrays and Static Values

Not everything in Java is a Collection. If you're dealing with a raw array, you can't just call .stream() because arrays don't have methods. Instead, you use the Arrays utility class. I've seen people try to wrap arrays in a list just to get a stream, but that's an unnecessary detour.

String[] tags = {"Java", "Streams", "Functional"};
Stream<String> tagStream = Arrays.stream(tags);

Sometimes, you don't even have a data structure yet—you just have a few known values you want to process through a pipeline. In those cases, Stream.of() is your best friend. It's a convenience method that takes a varargs input and wraps it into a stream for you. It's much cleaner than creating a temporary list just to stream it.

Stream<Integer> numberStream = Stream.of(10, 20, 30, 40);

Generating Sequences on the Fly

Every now and then, you'll need a stream that isn't based on a fixed set of data. This is where Stream.iterate and Stream.generate come in. I'll warn you right now: these create infinite streams. If you try to collect an infinite stream into a list without a limit, your application will happily eat all your available RAM until it crashes.

Stream.iterate is perfect when the next value depends on the previous one. For example, if you need a stream of even numbers starting from 0, you provide a starting value and a lambda expression to calculate the next step:

Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2);

On the other hand, Stream.generate is for when you want a stream of completely independent values, like random numbers or a constant heartbeat signal. You pass it a Supplier, and it just keeps calling that supplier every time the stream asks for a new element.

Stream<Double> randoms = Stream.generate(Math::random);

Just remember: whenever you use iterate or generate, you must eventually use a method like .limit(n) to tell Java when to stop. Otherwise, your pipeline will never end.




📋 Practical Task

Exercise: Building a Product ID Generator

You are tasked with creating a system that generates a sequence of unique Product IDs for a warehouse inventory system. The IDs should follow a specific pattern: they start at 1000 and increase by 1 for every product created.

Write a program that does the following:

  • Use Stream.iterate to create an infinite stream of integers starting at 1000.
  • Limit the stream to exactly 15 elements (representing the first 15 products).
  • Convert these integers into Strings, prefixing each one with "PROD-". (Hint: You can use .map() or a simple loop to print them, but the core of the generation must be the Stream).
  • Print each generated Product ID to the console.

Expected Output:
PROD-1000
PROD-1001
...
PROD-1014

Rating
0 0

There are no comments for now.

to be the first to leave a comment.