Java 8 Features: Lambdas and the Stream API

Welcome back, Java enthusiasts! Today, we’re diving into some of the most significant features introduced in Java 8: Lambda expressions and the Stream API. These two features brought a functional programming style to Java, which can significantly improve code readability and reduce boilerplate code.

Lambda Expressions

Lambda expressions allow you to treat functionality as a method argument. They enable the implementation of functional interfaces (an interface with a single abstract method) in a more compact and expressive way.

What is a Lambda Expression?

A lambda expression is defined using a combination of parameters and a body. The general syntax looks like this:

(parameters) -> expression

Or, if the body has multiple statements:

(parameters) -> { statements }

Example of a Lambda Expression

import java.util.Arrays;
import java.util.List;

public class LambdaExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

        // Using a Lambda expression to print names
        names.forEach(name -> System.out.println(name));
    }
}

In this example, we use a lambda expression to iterate over a list of names and print each one. The expression name -> System.out.println(name) is passed to the forEach method.

Functional Interfaces

A functional interface is an interface that contains only one abstract method. In Java 8, several built-in functional interfaces are provided in the java.util.function package, such as:

  • Consumer: Represents an operation that accepts a single input argument and returns no result.
  • Supplier: Represents a supplier of results, with no input arguments.
  • Function: Represents a function that takes an argument and produces a result.
  • Predicate: Represents a predicate (boolean-valued function) of one argument.

Example Using Functional Interfaces

import java.util.function.Consumer;

public class FunctionalInterfaceExample {
    public static void main(String[] args) {
        Consumer<String> greet = name -> System.out.println("Hello, " + name);
        greet.accept("Alice");  // Output: Hello, Alice
    }
}

In this example, we create a Consumer functional interface that takes a String and prints a greeting.

The Stream API

The Stream API provides a powerful way to process sequences of elements (e.g., collections) in a functional style. Streams allow you to perform operations such as filtering, mapping, and reducing without modifying the underlying data structure.

Creating Streams

Streams can be created from various data sources, including collections, arrays, or I/O channels.

Example of Creating a Stream from a List

import java.util.Arrays;
import java.util.List;

public class StreamCreationExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
        names.stream().filter(name -> name.startsWith("A"))
             .forEach(System.out::println); // Output: Alice
    }
}

Here, we create a stream from the names list, filter it for names starting with “A,” and print the result.

Stream Operations

The Stream API includes two types of operations: intermediate and terminal.

  • Intermediate Operations: These operations return a new stream, allowing for method chaining. Examples include filter(), map(), and sorted().
  • Terminal Operations: These operations produce a result or a side effect and terminate the stream. Examples include forEach(), collect(), and reduce().

Example of Stream Operations

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

public class StreamOperationsExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

        List<String> filteredNames = names.stream()
                                             .filter(name -> name.length() > 3)
                                             .map(String::toUpperCase)
                                             .collect(Collectors.toList());

        System.out.println(filteredNames); // Output: [ALICE, CHARLIE]
    }
}

In this example, we filter the names to include only those longer than three characters, transform them to uppercase, and collect the results into a new list.

Conclusion

Java 8’s introduction of Lambda expressions and the Stream API revolutionized how we write Java code, making it more concise and expressive. By leveraging these features, you can improve code readability and write efficient data processing logic.

Want to learn more about Java Core? Join the Java Core in Practice course now!

To learn more about ITER Academy, visit our website.

Scroll to Top