Stream Api

What is a Stream?

A Stream in Java is like a flow of data. Imagine water flowing through a pipe; the water is the data, and the pipe is the stream. You can think of streams as a way to work with lots of data one piece at a time without storing it all in memory.

Why Use Streams?

Streams help you work with collections (like lists and sets) in a clean and efficient way. They let you perform operations like filtering, sorting, and transforming data with less code.

Key Parts of a Stream

  1. Source:

    • This is where the stream gets its data. It can come from a list, array, file, or other places.
  2. Intermediate Operations:

    • These are steps that change or filter the stream. They are like checkpoints where you can modify the data. These operations are lazy, meaning they only work when you tell the stream to finish (with a terminal operation).
    • Examples: filter, map, sorted.
  3. Terminal Operations:

    • These are steps that finish the stream and produce a result. When you call a terminal operation, it processes all the intermediate steps.
    • Examples: forEach, collect, reduce.

Creating a Stream

You can create a stream from different sources:

  • From a List:

    List list = Arrays.asList("apple", "banana", "cherry");
    Stream stream = list.stream();
  • From an Array:

    String[] array = {"apple", "banana", "cherry"};
    Stream stream = Arrays.stream(array);
  • From a File:

    Stream stream = Files.lines(Paths.get("file.txt"));

Intermediate Operations

Intermediate operations transform the stream but do not produce a final result. Here are some common ones:

  • filter:

    • Keeps only the elements that match a condition.
      Stream filteredStream = stream.filter(s -> s.startsWith("a"));
  • map:

    • Changes each element using a function.
      Stream lengthStream = stream.map(String::length);
  • sorted:

    • Sorts the elements.
      Stream sortedStream = stream.sorted();

Terminal Operations

Terminal operations produce a result or side effect. Here are some examples:

  • forEach:

    • Does something with each element, like printing it.
      stream.forEach(System.out::println);
  • collect:

    • Gathers the elements into a collection, like a list.
      List result = stream.collect(Collectors.toList());
  • reduce:

    • Combines all elements into one, like adding numbers together.
      Optional concatenated = stream.reduce((s1, s2) -> s1 + s2);

Example in Simple Steps

Here’s a complete example to show how it works:

  1. Start with a List:

    List fruits = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
  2. Create a Stream:

    Stream fruitStream = fruits.stream();
  3. Apply Intermediate Operations:

    • Filter to keep fruits that start with "a" or "e":

      Stream filteredStream = fruitStream.filter(s -> s.startsWith("a") || s.startsWith("e"));
    • Sort the filtered fruits:

      Stream sortedStream = filteredStream.sorted();
  4. Apply a Terminal Operation:

    • Collect the sorted fruits into a list:
      List result = sortedStream.collect(Collectors.toList());
  5. Print the Result:

    result.forEach(System.out::println);

Putting It All Together

Here’s the entire example in one piece:

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

public class StreamExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");

        List<String> result = fruits.stream()       // Create a stream from the list
                                    .filter(s -> s.startsWith("a") || s.startsWith("e")) // Filter the stream
                                    .sorted()       // Sort the stream
                                    .collect(Collectors.toList()); // Collect the result into a list

        result.forEach(System.out::println); // Print each element in the result list
    }
}

Parallel Streams

If you have a lot of data and want to process it faster, you can use parallel streams. This makes the stream operations run on multiple threads.

List<String> list = Arrays.asList("apple", "banana", "cherry");
Stream<String> parallelStream = list.parallelStream();

Parallel streams can speed up processing but can also be tricky if not used correctly. They work best when operations are independent and can run safely at the same time.

Conclusion

The Stream API in Java helps you work with data in a simple, readable, and efficient way. By using streams, you can write less code and make it easier to understand and maintain. The key is to use intermediate operations to transform the data and terminal operations to produce the final result.

Leave a Comment

Your email address will not be published. Required fields are marked *