Creating Streams

Author      Ter-Petrosyan Hakob

Streams in Java let you work with data in a flexible way. Instead of writing loops again and again, you can describe what you want to do with the data, and the stream takes care of the rest.

If you already have a collection (like a List or Set), you can turn it into a stream easily:

List<String> colors = List.of("red", "blue", "yellow");
Stream<String> colorStream = colors.stream();

For arrays, use Stream.of or Arrays.stream:

String[] colors = {"red", "blue", "yellow"};
Stream<String> colorStream = Stream.of(colors);

If you only want part of an array, say the first three numbers:

int[] numbers = {1, 2, 3, 4, 5};
IntStream firstThree = Arrays.stream(numbers, 0, 3);

Sometimes you want an empty stream, for example as a default value. Use:

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

Streams don’t have to be finite! You can make streams that keep producing data. There are two common ways:

generate takes a function with no input and keeps calling it.

Stream<String> echoes = Stream.generate(() -> "Hello!");
Stream<Double> randomNumbers = Stream.generate(Math::random);

The first one repeats "Hello!" forever, the second one gives endless random numbers.

iterate starts with a seed (first value) and applies a function to create the next ones.

Stream<Integer> counting = Stream.iterate(0, n -> n + 1);

This creates numbers: 0, 1, 2, 3, ....

To stop it, use a condition:

Stream<Integer> upToFive = Stream.iterate(0, n -> n < 5, n -> n + 1);

This produces 0, 1, 2, 3, 4.

String text = "Hello\nHola\nCiao";
Stream<String> greetings = text.lines();

If you want to process a file line by line:

try (Stream<String> lines = Files.lines(Path.of("data.txt"))) {
    lines.forEach(System.out::println);
}

The try block makes sure the file closes properly.

If you need to build a stream step by step, use Stream.Builder:

Stream.Builder<Integer> builder = Stream.builder();
builder.add(10);
builder.add(20);
builder.add(30);
Stream<Integer> numbers = builder.build();

If you only have an Iterator or Iterable (not a collection), you can still make a stream:

Stream<String> fromIterator = StreamSupport.stream(Spliterators
        .spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);

If you have an Iterable that is not a collection, you can turn it into a stream by callin

StreamSupport.stream(iterable.spliterator(), false);

Streams work on top of collections. If you change the collection while the stream is running, strange errors may happen.

Safe (though unusual):

List<String> items = new ArrayList<>(List.of("A", "B"));
Stream<String> s = items.stream();
items.add("C"); 
long count = s.count(); // Works, but not recommended

Unsafe:

List<String> items = new ArrayList<>(List.of("A", "B"));
Stream<String> s = items.stream();
s.forEach(x -> items.remove(x)); // Error