Stream Creation

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 5 min

TL;DR

  • Streams do not store data; they must be created from a Data Source.
  • Common sources include Collections, Arrays, specific values, or files.
  • The most common way to create a stream is by calling .stream() on a Collection.

Concept

A Stream is an abstraction, not a physical data structure. To use the Stream API, you must first attach a stream to a source of data. Once the stream is created, you can apply your pipeline of intermediate and terminal operations to process the data.

Examples

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamCreation {
    public static void main(String[] args) {
        
        // 1. From a Collection (Most common)
        List<String> list = Arrays.asList("A", "B", "C");
        Stream<String> stream1 = list.stream();
        
        // 2. From an Array
        String[] arr = {"A", "B", "C"};
        Stream<String> stream2 = Arrays.stream(arr);
        
        // 3. From individual values
        Stream<String> stream3 = Stream.of("A", "B", "C");
        
        // 4. Using Stream.builder()
        Stream<String> stream4 = Stream.<String>builder()
            .add("A").add("B").add("C").build();
            
        // 5. Infinite Streams (Using iterate or generate)
        // Generates an infinite stream of 1s.
        // MUST use .limit() or it will run forever!
        Stream<Integer> stream5 = Stream.generate(() -> 1).limit(5);
        
        // Starts at 0, adds 2 each time.
        Stream<Integer> stream6 = Stream.iterate(0, n -> n + 2).limit(5);
    }
}

Interview Questions

Q: Can you create a Stream from primitive types?
A: Yes, but you shouldn’t use Stream<T> because it will cause boxing/unboxing overhead (e.g., converting int to Integer). Instead, Java provides primitive-specific stream interfaces: IntStream, LongStream, and DoubleStream. You can create them using IntStream.range(1, 10) or Arrays.stream(new int[]{1, 2, 3}).

Q: What happens if you modify the underlying collection after creating a Stream from it, but before executing the terminal operation?
A: Streams are late-binding. If you modify the underlying collection before the terminal operation begins execution, those modifications will be visible to the stream. However, if you modify the collection during the execution of the stream pipeline (e.g., inside a .forEach()), it will throw a ConcurrentModificationException.