Streams and Lambdas in Java: A Guide to Functional Programming with Examples

myexamcloud

MyExamCloud

Posted on August 16, 2024

Streams and Lambdas in Java: A Guide to Functional Programming with Examples

Functional programming is a widely used approach in modern software development. Java, being a language that supports both object-oriented and functional programming, introduced streams and lambdas in its 8th version. These features allow developers to apply functional style to collections, making operations on collections more efficient and clean.

Streams in Java
Streams in Java turn a collection into a flow of elements. They enable developers to perform functional operations on collections such as mapping, filtering, and reducing. Streams are best used for operations that involve transformation, aggregation, or filtering of data.

An example of using streams is sorting a list of names in alphabetical order. We can create a list of names and then use streams to sort them as shown below:

List<String> names = Arrays.asList("John", "Mary", "David", "Emily", "Tom");
names.stream().sorted().forEach(System.out::println); //output: David, Emily, John, Mary, Tom

Here, we have created a stream using the stream() method on the list. We have then applied the sorted() method to sort the elements in the stream in natural order. Finally, the forEach() method is used to print each element in the stream.

Lambdas in Java
Lambdas are used to define first-class functions in Java. These functions can consume elements from a stream and perform operations on them. Lambdas are defined outside of a class and can be passed as arguments to functions.

To use lambdas with streams, we need to understand the concept of functional interfaces. These are interfaces that have only one abstract method. Lambda expressions can be used to provide a concrete implementation for the abstract method of a functional interface.

Sort a list of names using lambdas
Building on the example above, let's sort the names in descending order using lambdas.

List<String> names = Arrays.asList("John", "Mary", "David", "Emily", "Tom");
names.stream().sorted((a, b) -> b.compareTo(a)).forEach(System.out::println)); //output: Tom, Mary, John, Emily, David

Here, we have used a lambda expression as an argument to the sorted() method. This lambda function accepts two arguments (a and b) and compares them, returning a positive, negative, or zero value based on the comparison. This allows us to sort the elements in descending order by using the compareTo() method on the elements.

Turn a stream back into a collection
In some cases, we may want to turn a stream back into a collection after performing operations on it. We can do this using the collect() method, which takes in a Collector object as an argument.

List<String> filteredNames = names.stream()
.filter(name -> name.length() > 4) //keep only names with length > 4
.collect(Collectors.toList()); //return a list of filtered names

The collect() method takes a functional interface of type Collector and uses it to accumulate elements into a collection. In the example above, we have used the toList() method from the Collectors class to create a list of filtered names.

Other operations on streams
Apart from sorting and filtering, there are other operations that can be performed on streams such as mapping, reducing, and iteration. These operations allow us to transform, reduce, and iterate over the elements in a stream respectively.

Map and reduce a stream of numbers
Let's say we have a list of numbers and we want to find the sum of all the even numbers. We can do this using the map() and reduce() methods.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int sum = numbers.stream()
.filter(num -> num % 2 == 0) //filter out odd numbers
.map(num -> num * 2) //double the even numbers
.reduce(0, (a, b) -> a + b); //add all the numbers together

Here, we have used the map() method to double the even numbers in the stream and then used the reduce() method to add all the numbers together. The result is a sum of all the even numbers in the list.

Reuse operations on streams
In some cases, we may need to use the same functional operation on multiple streams. To avoid writing the same code over and over again, we can create a functional interface and pass it as an argument to the methods.

Let's take the example of sorting a list of names by last name. We can create a comparator class that implements the Comparator interface and pass it as an argument to the sorted() method.

static class LastNameComparator implements Comparator<String> {
@Override
public int compare(String name1, String name2) {
String[] parts1 = name1.split(" ");
String lastName1 = parts1[parts1.length - 1];
String[] parts2 = name2.split(" ");
String lastName2 = parts2[parts2.length - 1];
return lastName2.compareTo(lastName1);
}
}

names.stream().sorted(new LastNameComparator()).forEach(System.out::println); //output: Emily, David, John, Mary, Tom

Here, we have passed the LastNameComparator as an argument to the sorted() method to sort the names by last name.

Conclusion
Streams and lambdas in Java provide a powerful and efficient way to perform functional operations on collections. They enable developers to write code that is clean, concise, and reusable. By understanding the concepts of streams and lambdas, we can improve our overall coding skills and write more efficient code.

One effective method for improving your Java skills is by obtaining the latest Java Certification and using the 1Z0-830 Practice Tests available on MyExamCloud.

💖 💪 🙅 🚩
myexamcloud
MyExamCloud

Posted on August 16, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related