Need of Lambda Expressions – Continued

In my previous post Lambda Expressions – Need of Lambda Expressions (Java 8), I talked about the example where we created a Thread using Lambda Expressions. Lambda Expressions are not just to fill up limitations of Anonymous Classes. Lambda Expressions also help to internally iterate through an array or collection.

Internal Iteration? What?… Let’s see.

13
14
15
16
17
18
19
20
21
List<Student> students = ...
double highestScore = 0.0;
for(Student s: students){
    if(s.getGradYear() == 2011){
        if(s.getScore() > highestScore){
            highestScore = s.getScore();
        }
    }
}

We see this kind of code a lot in Java. What’s happening here is we made a generic List students of type Student. We assume each student has gradYear and score members and we have getters getGradYear() and getScore() respectively for these members. What we are trying to do is that we’re iterating through the students list and we’re trying to get the highest score of a Student whose graduation year is 2011. We are using the well known for (each) loop with a couple of conditional statements.

Although there’s nothing wrong with the above code functionally, there are some problems in the way we process this if we want to take advantage of multiple cores and multiple processors. The problems are:

  1. Our code is controlling the iteration.
  2. Inherently Serial: iterates from beginning to the end.
  3. Not Thread-Safe.

Let’s have a closer look at the problems. Firstly, we are controlling the iteration. It’s we who define the order of iteration. Although in this code order doesn’t matter much, but if there were some part of code where order mattered, we couldn’t break it down to parallel operations on different Threads. By breaking up code into parallel operations, we would introduce non-deterministic behavior. Different threads would execute at different times, they might be looking at different data. Like, we have a member variable highestScore. Different threads might be trying to modify highestScore at the same time and may overwrite the data provided by other Threads. Hence we say, this code is not Thread-Safe. To prevent such situations we would need to introduce locking to prevent multiple Threads from accessing the variable at same time and that would only add more code. As we all know, we the programmers, are the laziest. So whats a better way to ensure Multi-Threading and parallel execution?

A more functional approach would be better in such scenarios. Let’s see how.

13
14
15
16
17
18
19
20
21
22
23
24
double highestScore = students
    .filter(new Predicate(){
        public boolean op(Student s){
            return s.getGradYear() == 2011;
        }
    })
    .map(new Mapper<Student,Double>(){
        public Double extract(Student s){
            return s.getScore();
        }
    })
    .max();

Now, what we’re doing is we’re taking a collection of students and passing it to a filter() method. The filter() method is told how to do the filtering using a Predicate Interface. Predicate interface has a single method and we’re defining anonymous inner class to tell what that method is going to do. In short, the filter method filters the students collection to give us only those students whose graduation year is 2011. The output of this filter method would be a subset of the students collection. Now that subset of data is passed into another method. The map() method uses another anonymous inner class Mapper to determine how to do the mapping. In this case, it has a single method called extract() which takes a Student as parameter and returns the score as a Double. So we take the collection of Students and convert that into a collection of records of the scores of those students. So at the end of the map, we have all the scores of the students who graduated in 2011. Now we have the max() method that determines the maximum of those scores and saves that in the highestScore variable.

That’s a lot of code and a lot more complicated stuff happening. There are various benefits of this approach.

  1. Iteration is handled by the library.
  2. Not inherently serial.
  3. Thread-Safe (client logic is stateless).

But with all these benefits, any newbie would give up programming seeing this syntax. Syntactically, there is a lot of unnecessary code here. A lot of keystrokes can be reduced if we use lambda expressions here. Let’s see how that would look like.

13
14
15
16
17
List<Student> students = ...
double highestScore = students
    .filter(Student s -> s.getGradYear()==2011)
    .map(Student s -> s.getScore())
    .max();

We again see that lambda expressions iterate through Collections internally and is syntactically a lot more cleaner than anonymous classes. It is Thread-Safe as the logic is stateless.

This was all about why we need Lambda Expressions.

We’ll cover syntax and further topics in coming blogs. Till then, keep learning.

Share this:
Share
«
»

Leave a Reply