Explore Streams in Java 8: Many ways to build streams


People, you have to know how to avoid suspicion…

Introduction at the beginning

Java 8 introduces Stream as a new feature, which is a functional programming method for processing collection data. Stream provides a more concise, efficient and easy-to-understand method to operate collection data, while also enabling parallel processing to improve performance.

The following are some important features and usage of Stream:

  1. Stream creation: Streams can be created from various data sources such as collections, arrays, and I/O channels. For example, use the Collection.stream() method to convert a collection into a stream, and use Arrays.stream() to convert an array into a stream.

  2. Intermediate operations: Stream streams support various intermediate operations, which allow filtering, mapping, sorting and other operations on elements in the stream without modifying the original data. Some common intermediate operations include filter (filter elements), map (map elements), sorted (sort elements), etc.

  3. Terminal operations: Terminal operations are the final operations on a stream, they trigger the actual computation and produce the result. Some common terminal operations include forEach (iterate through elements and perform operations), collect (collect elements from a stream into a collection), count (calculate the number of elements) etc.

  4. Lazy execution: Stream operations are lazily executed, which means that intermediate operations can be chained together without actual computation. The processing of the stream is only triggered when a terminal operation is called.

  5. Parallel processing: Stream stream supports parallel processing. By using the parallelStream() method, you can easily distribute the processing of the stream to multiple processor cores to improve performance.

  6. Functional programming style: Stream streams encourage the use of functional programming style, where operations are passed in the form of lambda expressions, making the code more expressive and concise.

Here is an example that demonstrates how to use a Stream to operate on a collection:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

int sum = numbers.stream()
                .filter(n -> n % 2 == 0) // Filter even numbers
                .map(n -> n * 2) // Double even numbers
                .reduce(0, Integer::sum); // Sum

System.out.println("The sum of doubled even numbers is: " + sum);

This is just a simple example of Stream, which shows some common operations of Stream, such as filtering, mapping and aggregation. Through Stream flow, collection data can be processed in a more concise and readable way, reducing boilerplate code and improving code quality and maintainability. Below we will give a detailed explanation of the various ways to build flows.

1. Create a stream from a collection

Creating a Stream from a collection is very simple. You can use the stream() method of the collection class to obtain a Stream object. Below I will show how to create a Stream from a collection and provide two code examples based on practical applications.

Example 1: Create Stream from List

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

public class StreamCreationExample {<!-- -->

    public static void main(String[] args) {<!-- -->
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");
        names.add("Eve");

        //Create a Stream stream
        Stream<String> nameStream = names.stream();

        //Use Stream stream to operate
        nameStream
            .filter(name -> name.startsWith("A"))
            .forEach(System.out::println);
    }
}

In this example, we first create a List collection containing some names, and then use the names.stream() method to create a Stream. Next, we use the filter intermediate operation to filter out names starting with “A”, and use the forEach terminal operation to print the output results.

Example 2: Create Stream from Map

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

public class StreamCreationFromMapExample {<!-- -->

    public static void main(String[] args) {<!-- -->
        Map<Integer, String> studentMap = new HashMap<>();
        studentMap.put(1, "Alice");
        studentMap.put(2, "Bob");
        studentMap.put(3, "Charlie");
        studentMap.put(4, "David");
        studentMap.put(5, "Eve");

        //Create a Stream from the Map's key collection
        Stream<Integer> studentIdsStream = studentMap.keySet().stream();

        //Use Stream stream to operate
        studentIdsStream
            .filter(id -> id % 2 == 0)
            .forEach(id -> System.out.println(id + ": " + studentMap.get(id)));
    }
}

In this example, we create a Map containing student IDs and names, and then use the studentMap.keySet().stream() method to create a Stream from the Map’s key set. Next, we use the filter intermediate operation to filter out even-numbered student IDs, and use the forEach terminal operation to print out the corresponding student information.

2. Create a stream from an array

Creating a Stream from an array is also very simple. Java 8 provides the Arrays.stream() method, which allows you to convert an array into a Stream. Below I will detail how to create a Stream from an array and provide two code examples.

Example 1: Create a Stream from an array of integers

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

public class StreamCreationFromArrayExample {<!-- -->

    public static void main(String[] args) {<!-- -->
        int[] numbers = {<!-- -->1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        //Create an IntStream from an array of integers
        IntStream numberStream = Arrays.stream(numbers);

        //Use Stream stream to operate
        int sum = numberStream
            .filter(n -> n % 2 == 0)
            .map(n -> n * 2)
            .sum();

        System.out.println("The sum of doubled even numbers is: " + sum);
    }
}

In this example, we first create an array of integers numbers and then convert it to an IntStream using the Arrays.stream(numbers) method flow. We then use this stream to perform a series of operations, including filtering out the even numbers, doubling them, and finally calculating their sum.

Example 2: Create a Stream from a string array

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

public class StreamCreationFromArrayExample {<!-- -->

    public static void main(String[] args) {<!-- -->
        String[] words = {<!-- -->"apple", "banana", "cherry", "date", "elderberry"};

        // Create Stream from string array
        Stream<String> wordStream = Arrays.stream(words);

        //Use Stream stream to operate
        wordStream
            .filter(word -> word.startsWith("b"))
            .map(String::toUpperCase)
            .forEach(System.out::println);
    }
}

In this example, we create an array of strings words and then convert it to a Stream using the Arrays.stream(words) method flow. Next, we use the stream to perform operations, including filtering out words starting with the letter “b”, converting them to uppercase and printing them.

3. Static factory method

Java 8 also introduced some static factory methods to create Stream streams, which make creating Stream streams easier and more flexible. Below I’ll describe these static factory methods in detail and provide two code examples.

Static factory method creates Stream

  1. Stream.of(T... values): Creates a variadic element list by passing it to the Stream.of method A Stream containing these elements. This is very convenient for creating streams with a small number of elements.

  2. Stream.empty(): Use the Stream.empty() method to create an empty Stream.

  3. Stream.generate(Supplier s): Create an infinite-size Stream by providing a Supplier function, which generates element. Typically, you need to use the limit operation to limit the number of elements generated.

  4. Stream.iterate(T seed, UnaryOperator f): Create an infinite sequence by providing an initial value (seed) and a unary operation function (UnaryOperator) Stream. For example, you can use Stream.iterate(0, n -> n + 1) to create a Stream of a sequence of natural numbers.

Example 1: Use Stream.of to create a Stream

import java.util.stream.Stream;

public class StreamFactoryExample {<!-- -->

    public static void main(String[] args) {<!-- -->
        // Use Stream.of to create a Stream stream
        Stream<String> stream = Stream.of("Apple", "Banana", "Cherry", "Date");

        //Print the elements in the Stream
        stream.forEach(System.out::println);
    }
}

This example uses the Stream.of static factory method to create a Stream containing the names of the fruits, and uses the forEach terminal operation to print out the name of each fruit.

Example 2: Use Stream.generate to create a Stream

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

public class StreamGenerateExample {<!-- -->

    public static void main(String[] args) {<!-- -->
        // Use Stream.generate to create a stream of random integers
        Stream<Integer> randomIntStream = Stream.generate(() -> new Random().nextInt(100));

        //Limit the number of elements in the stream and print
        randomIntStream
            .limit(10)
            .forEach(System.out::println);
    }
}

In this example, we use the Stream.generate static factory method to create a Stream containing random integers. Then, we use the limit operation to limit the number of elements in the stream, and finally print out the generated random integer.

These static factory methods provide a convenient way to create different types of Streams, making stream creation more flexible and convenient.

4.Use Stream.Builder

Stream.Builder is a way to build streams introduced in Java 8. It allows adding elements to the stream one by one and ultimately building a Stream object. This is useful for building streams while iterating or generating elements.

The following is a detailed introduction to how to use Stream.Builder to create a stream, and two code examples are provided.

Steps to create a stream using Stream.Builder:

  1. Create Stream.Builder object: First, you need to create a Stream.Builder object.

  2. Add elements: Then, use the add method of Stream.Builder to add elements to the stream one by one.

  3. Build Stream: Once all elements have been added, you can call the build method of Stream.Builder to build the Stream object.

Example 1: Use Stream.Builder to create a stream and filter odd numbers

import java.util.stream.Stream;

public class StreamBuilderExample {<!-- -->

    public static void main(String[] args) {<!-- -->
        Stream.Builder<Integer> builder = Stream.builder();

        //Add elements to Stream
        for (int i = 1; i <= 10; i + + ) {<!-- -->
            builder.accept(i);
        }

        //Construct Stream
        Stream<Integer> numberStream = builder.build();

        //Use Stream operation
        numberStream
            .filter(n -> n % 2 == 0) // Filter even numbers
            .forEach(System.out::println);
    }
}

In this example, we first create a Stream.Builder object, then use the accept method to add integers from 1 to 10 to the stream one by one, and finally use filter The intermediate operation filters out the even numbers and uses the forEach terminal operation to print out the result.

Example 2: Use Stream.Builder to generate the Fibonacci sequence

import java.util.stream.Stream;

public class FibonacciStreamExample {<!-- -->

    public static void main(String[] args) {<!-- -->
        Stream.Builder<Long> builder = Stream.builder();

        long a = 0, b = 1;
        int count = 10;

        for (int i = 0; i < count; i + + ) {<!-- -->
            builder.accept(a);
            long next = a + b;
            a = b;
            b = next;
        }

        Stream<Long> fibonacciStream = builder.build();

        fibonacciStream.forEach(System.out::println);
    }
}

In this example, we use Stream.Builder to generate the first 10 numbers of the Fibonacci sequence. We first create a Stream.Builder object, then use a loop to add the elements of the Fibonacci sequence one by one, and finally use the forEach terminal operation to print out the results.

Stream.Builder is suitable for situations where you need to generate elements one by one and build a stream, making the code clearer and more flexible.

5. Create a stream from a file

In Java, a Stream can be created from a file for file reading and processing. Usually, you can use the classes in the java.nio.file package to achieve this purpose. Here’s how to create a stream from a file, along with two code examples:

Method 1: Use the Files.lines method to create a stream of text files

The Files.lines method allows the creation of a Stream containing the file content, suitable for line-by-line reading of text files. This method accepts a file path as a parameter and returns a Stream object.

Here’s an example:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class FileToStreamExample {<!-- -->

    public static void main(String[] args) {<!-- -->
        String filePath = "sample.txt"; // File path

        try (Stream<String> lines = Files.lines(Paths.get(filePath))) {<!-- -->
            lines.forEach(System.out::println); //Print file content line by line
        } catch (IOException e) {<!-- -->
            e.printStackTrace();
        }
    }
}

In this example, we use the Files.lines method to open a text file named “sample.txt” and print its contents to the console line by line.

Method 2: Use the Files.newInputStream method to create a binary file stream

If you want to process binary files, such as images or audio files, you can use the Files.newInputStream method to create an InputStream and then convert it to a Stream. Here’s an example:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

public class BinaryFileToStreamExample {<!-- -->

    public static void main(String[] args) {<!-- -->
        String filePath = "image.jpg"; // Binary file path

        try {<!-- -->
            Path path = Paths.get(filePath);
            Stream<Byte> byteStream = Files.newInputStream(path)
                                          .map(b -> (byte) b);

            byteStream.forEach(System.out::println); // Print the contents of the binary file byte by byte
        } catch (IOException e) {<!-- -->
            e.printStackTrace();
        }
    }
}

In this example, we use the Files.newInputStream method to create an input stream, then map it to a Stream, and finally print the contents of the binary file byte by byte .

Whether it is a text file or a binary file, it is very useful to create a Stream from a file, which makes the reading and processing of files more convenient and flexible. When working with files, don’t forget to properly handle any IOException exceptions that may arise.