Introduction to Lambda Expressions and Streams in Java

Shivani Bagalwadi
5 min readMar 31, 2022

--

Lambda Expressions and Streams were added in java 8. Both support Functional programming which was introduced in Java 8.

But what is Functional Programming?.. Functional programming is a paradigm that allows programming using expressions i.e. declaring functions, passing functions as arguments and using functions as statements. It helps in writing clear and concise code, which is easily maintainable (in some scenarios). (If you want to dig deeper into functional programming you can use this link.)

In this blog we will be going through the basics of lambda expressions and streams in java, its usage and importance. The blog assumes you have prior knowledge of java, no knowledge of lambda expressions and streams is expected.

Introduction to Lambda Expressions

Introduction of lambda expressions in java 8 enabled us to write concise code. This is done by eliminating the need to create an implementation of the interface.

Before we look at lambda expression, we need to cover another concept introduced in java 8…. that is Functional Interfaces.

An Interface that contains exactly one abstract method is known as functional interface. It can have any number of default, static methods but can contain only one abstract method.

Lambda expressions are used with functional interfaces to implement this only one abstract method that is present in functional interface and therefore implement function interface.

A lambda expression is a short form for writing an anonymous class. For example consider the functional interface below.

@FunctionalInterface
interface Age {
int x = 21;
void getAge();
}

When using anonymous function to implement the getAge() method.

class Test{
public static void main(String args[]){
Age age = new Age(
@Override
public void getAge()
{
// some operation
System.out.print("inside Age function");
});
}

When using lambda expressions

class Test {
public static void main(String args[])
{
Age age = () -> System.out.println("Inside age function");
// This will print "inside age function"
age.getAge(5);
}
}

In the above example using anonymous inner class and lambda expression we can see that the size of code is significantly reduced when using lambda expressions compared to anonymous class.

Basic Syntax of lambda Expression

The simplest lambda expression contains a single parameter and an expression.

parameter -> expression

To use more than one parameter, wrap them in parentheses:

(parameter1, parameter2) -> expression

Expressions are limited. They have to immediately return a value, and they cannot contain variables, assignments or statements such as if or for. In order to do more complex operations, a code block can be used with curly braces. If the lambda expression needs to return a value, then the code block should have a return statement.

(parameter1, parameter2) -> { code block }

As you can create custom functional interfaces, there are multiple built in functional Interfaces in java, some of them are Runnable, Comparator, Consumer, Predicate etc.

Lets see usage of lambda expressions

  1. Using Consumer interface in java
import java.util.ArrayList;

public class Main {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(5);
numbers.add(9);
numbers.add(8);
numbers.add(1);
numbers.forEach( (n) -> { System.out.println(n); } );
}
}

In the example above the forEach method accepts implementation of Consumer interface in java. Consumer interface has an function named accept() which takes in one argument of type T and produces a result. However doesn't return any value.

In the above example instance of Consumer interface is passed to forEach function using lambda expression. Each number present in the parameter is passed as argument and is printed.

2. Lambda expression with no parameter (Runnable interface)

public class Main {
public static void main(String[] args) {
Runnable r2=()->{
System.out.println("Thread2 is running...");};
Thread t2=new Thread(r2);
t2.start();
}
}

In the above example lambda expression is used to implement runnable interface.

3. Lambda expression with multiple statement

@FunctionalInterface  
interface Sayable{
String say(String message);
}

public class Main{
public static void main(String[] args) {

Sayable person = (message)-> {
String str1 = "I would like to say, ";
String str2 = str1 + message;
return str2;
};

System.out.println(person.say("time is precious."));
}
}

Introduction to Streams

Introduced in Java 8, the Stream API is used to process collections of objects. A stream is a sequence of objects that supports various methods which can be pipelined to produce the desired result.

You can create a stream from array or list using following methods.(Java also provides additional methods to achieve this)

Stream<Integer> stream= Stream.of(1, 2,3, 4,5, 6,7, 8,9);

List<Employee> employeeList = new ArrayList<>();
Stream<Employee> emp = employeeList.stream()

There are various benefits of using streams in java over traditional loops….. Streams compact functions into fewer and more readable lines of code, Changes done to the stream does not reflect in the original collection object, stream supports parallel operations that often they run faster than sequential operations.

There are two types of operations supported on streams.

  1. Intermediate operations: These methods do not produce any results. They usually accept functional interfaces as parameters and always return a new stream.
  2. Terminal operations: These methods produce some results, e.g., count(), collect() etc.

Each intermediate operation is lazily executed and returns a stream as a result, hence various intermediate operations can be pipelined. The Terminal operations mark the end of the stream and return the result.

Examples of Intermediate operations

  1. map: The map method is used to returns a stream consisting of the results of applying the given function to the elements of this stream.
List number = Arrays.asList(2,3,4,5);
List square = number.stream()
.map(x->x*x)
.collect(Collectors.toList());

2. filter: The filter method is used to select elements as per the Predicate passed as argument.

List names = Arrays.asList("Reflection","Collection","Stream");
List result = names.stream()
.filter(s->s.startsWith("S"))
.collect(Collectors.toList());

3. sorted: The sorted method is used to sort the stream.

List names = Arrays.asList("Reflection","Collection","Stream");
List result = names.stream().sorted()
.collect(Collectors.toList());

Examples of Terminal operations

  1. collect: The collect method is used to return the result of the intermediate operations performed on the stream.
List number = Arrays.asList(2,3,4,5,3);
Set square = number.stream()
.map(x>x*x)
.collect(Collectors.toSet());

2. forEach: The forEach method is used to iterate through every element of the stream.

List number = Arrays.asList(2,3,4,5);
number.stream().map(x->x*x)
.forEach(y->System.out.println(y));

3. count: The count method is used to find the number of elements.

List number = Arrays.asList(2,3,4,5);
number.stream().map(x->x*x)
.filter(x->x>4).count();

Sample uses of streams

  1. Count occurrence of given character ‘ch’ in string
public static long count(String s, char ch){
return s.chars()
.filter(c -> c == ch)
.count();
}

2. Print String from a list of string in uppercase

List<String> nameList = Arrays.asList("Jayesh", "Dany", "Khyati", "Hello", "Mango");
nameList.stream()
.map(String::toUpperCase)
.forEach(System.out::println);

This was just an introduction of Stream API in java, it still provides many more features such as parallel Streams, infinite streams etc.. If you want to learn more about streams you can refer to following links.

Conclusion

Both Lambda Expressions and Streams are one of the important features which were introduced in Java 8 that reduce the number of lines of code required, improve readability of code and also increase performance (in some cases). Initially It might be difficult to understand or use these features everyday but once you start using it they provide various benefits.

References

  1. https://www.geeksforgeeks.org/lambda-expressions-java-8/
  2. https://www.geeksforgeeks.org/stream-in-java/
  3. http://tutorials.jenkov.com/java/lambda-expressions.html
  4. https://www.baeldung.com/java-8-streams

--

--

No responses yet