DEV Community

Priyank Bhardwaj
Priyank Bhardwaj

Posted on

Creating Java Streams

In the last part, we explored the Functional Interfaces — Predicate, Function, Consumer, Supplier and few more — that form the foundation of stream operations.

Now, it’s time to understand how streams are created. After all, before you can map, filter, or collect data, you need a stream source.

What Is a Stream Source?

A Stream in Java doesn’t store data itself. It’s a sequence of elements sourced from:

  • Collections
  • Arrays
  • Static factory methods (Stream.of())
  • Functions that generate infinite streams

1. From Collections

The most common way to get a stream is from a Collection (like List, Set, etc.).

Every collection in Java 8 and later comes with two methods:

stream() – creates a sequential stream

parallelStream() – creates a parallel stream for multi-threaded processing

import java.util.*;

public class StreamFromCollection {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Arjun", "Bhima", "Karna", "Nakul");

        // Sequential Stream
        System.out.println("Sequential Stream:");
        names.stream()
             .filter(name -> name.startsWith("K"))
             .forEach(System.out::println);

        // Parallel Stream
        System.out.println("\nParallel Stream:");
        names.parallelStream()
             .map(String::toUpperCase)
             .forEach(System.out::println);
    }
}
Enter fullscreen mode Exit fullscreen mode

OUTPUT

Sequential Stream:
Karna

Parallel Stream:
ARJUN
BHIMA
KARNA
NAKUL
Enter fullscreen mode Exit fullscreen mode

The order in parallel streams may vary depending on threads.

Takeaways:
Use stream() for predictable sequential processing, and parallelStream() when you can benefit from parallelism that is when performance matters over order and thread-safety.

2. From Arrays

The Arrays class provides a convenient stream() method:

import java.util.Arrays;
import java.util.stream.IntStream;

public class StreamFromArray {
    public static void main(String[] args) {
        String[] fruits = {"Apple", "Banana", "Cherry"};

        Arrays.stream(fruits)
              .map(String::toUpperCase)
              .forEach(System.out::println);

        int[] numbers = {1, 2, 3, 4, 5};
        IntStream intStream = Arrays.stream(numbers);
        intStream.forEach(System.out::println);
    }
}
Enter fullscreen mode Exit fullscreen mode

OUTPUT

APPLE
BANANA
CHERRY
1
2
3
4
5
Enter fullscreen mode Exit fullscreen mode

Why use Arrays.stream() instead of Stream.of() for arrays?
Because Stream.of(numbers) would create a stream of one element (the entire array), not a stream of its contents.

3. Using Stream.of()

Stream.of() can be used to create streams directly from values or object arrays.

import java.util.stream.Stream;

public class StreamOfExample {
    public static void main(String[] args) {
        Stream<String> gods = Stream.of("Indra", "Agni", "Vayu", "Varuna");
        gods.forEach(System.out::println);

        // Heterogeneous data
        Stream<Object> mixed = Stream.of("Hello", 42, true, 3.14);
        mixed.forEach(System.out::println);
    }
}
Enter fullscreen mode Exit fullscreen mode

OUTPUT

Indra
Agni
Vayu
Varuna

Hello
42
true
3.14
Enter fullscreen mode Exit fullscreen mode

When to use:
Use Stream.of() for small sets of elements or when you want a quick ad-hoc stream.

4. Infinite Streams — iterate() and generate()

Streams can also be infinite, producing values endlessly — unless you limit them using .limit(n).

Stream.iterate()

import java.util.stream.Stream;

public class IterateExample {
    public static void main(String[] args) {
        Stream.iterate(1, n -> n + 2)
              .limit(5)
              .forEach(System.out::println);
    }
}
Enter fullscreen mode Exit fullscreen mode

OUTPUT

1
3
5
7
9
Enter fullscreen mode Exit fullscreen mode

Explanation:

Stream.iterate(seed, f)

  • seed (1) → The initial value of the stream
  • f (n -> n + 2) → The function that computes the next element based on the previous one

So, the stream goes like:
1 → 3 → 5 → 7 → 9 → ... (infinite, but we limited it to 5)

Stream.generate()

import java.util.stream.Stream;
import java.util.Random;

public class GenerateExample {
    public static void main(String[] args) {
        Stream.generate(() -> new Random().nextInt(100))
              .limit(5)
              .forEach(System.out::println);
    }
}
Enter fullscreen mode Exit fullscreen mode

OUTPUT

42
17
83
9
65
Enter fullscreen mode Exit fullscreen mode

Explanation:

Stream.generate(supplier)

  • supplier → A function (no input, returns a value) that provides stream elements.
  • Here, we’re generating random integers continuously — .limit(5) ensures we only get 5 numbers.

💡 Important: Always use .limit() with iterate() or generate() — otherwise your program will run forever!


Summary

Source Type Example Stream Method Notes
Collection list.stream() Sequential stream Most common source
Array Arrays.stream(array) Converts array Works for primitives too
Values Stream.of(…) Direct stream Quick for literals
Infinite (iterate) Stream.iterate(1, n -> n + 1) Iterative stream Takes seed & function
Infinite (generate) Stream.generate(Math::random) Supplier stream Takes supplier only

What’s Next?

Now that we know how to create streams, in Part 4, we’ll dive into Intermediate Operations —
methods like map(), filter(), sorted() and more that transform your stream data step by step.

🔗 Next Up (Part 4): Intermediate Operations

Top comments (0)