Free Online Courses for Software Developers - MrBool
× Please, log in to give us a feedback. Click here to login
×

You must be logged to download. Click here to login

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

Java 8: Demystifying Lambdas

In this article, were gonna see some of the main concepts regarding Lambdas with Java 8

If we ask the developers about Java Java 8, we will have several well animated responses, especially on the use of lambda expressions.

But after a more honest conversation, find an earnestness mixed with a little fear in relation to new and mysterious APIs available on the web. Simon Ritter revealed some of the mysteries in the presentation of lambdas at QCon London 2014 conference.

The following is a Java code snippet that illustrates a common pattern:

List<Student> students = ...
Double highestScore = 0.0;
for (Student s : students) {
   if (s.gredYear == 2011) {
       if (s.score > highestScore) {
           highestScore = s.score;
       }
   }
}

Note: The parts in red are those that are interested; and parts in blue represent repetitive code.

In this issue, we want to find the highest score in a collection of students. We use a common language external iteration to browse and compare each element of the collection.

But there are some disadvantages in the code. First, the external iteration means that developers are responsible for implementing (imperative programming), and is used as a single loop, we set the code execution will be sequentially. If we want to optimize it, we could not easily target a set of executing instructions in parallel.

Second, the highestScore variable is mutable and is not thread-safe. So even if we were to break it into multiple threads, we would need to add a lock (lock) to prevent race conditions (competition), which in turn can introduce performance problems.

Now, if we want to act more intelligently, we can change a little further implementation into the functional style using an anonymous inner class:

List<Student> students = ...
Double highestScore = students.filter(new Predicate<Student>() {
   public boolean op(Student s) {
      return s.getGradYear() == 2011;
   }
}).map(new Mapper<Student, Double>() {
   public Double extract(Student s) {
      return s.getScore();
   }
}).max();

In this implementation, we eliminate mutable state and pass the work of interaction for the library. We're chaining a sequence of method calls to implement the operation in an expression "Look at all my students and filter only those who graduated in 2011".

The anonymous inner class implements the interface Predicate (it contains a method, accepts a parameter and returns a Boolean value) with the method "op", which simply compares the student's graduation year 2011 and returns the result.

We send the result (all graduate students in 2011) for the method "map", which will use another anonymous inner class to call a method of mapping interface with its unique method "extract" to extract the data you want (by calling the getScore ). Then we pass this result, which is a set of all graduate students notes in 2011 for the "max" method, which ultimately deliver the most value from the result set.

Using this approach, we treat all interaction, filter and accumulation using the library without doing so explicitly. This not only simplifies the implementation, but also eliminates state sharing, making it easier to ask the library code to break the implementation into several sub tasks and allocate it in different threads to execute in parallel. In many cases, also we perform further evaluation, saving even more time.

Then, an approach that uses anonymous inner class is fast and thread-safe, but watch out for the colors of the source code. Note that the amount of blue is larger than red, indicating repetitive code.

Then come into play lambda expressions!

Think of lambda expressions as a method, in that it accepts parameters, and returns body has a static type.

In this example, we use lambda expressions to obtain the same algorithm which determines the highest score, as in previous examples. Let's see in detail.

First, we create a stream from a Collection. The stream method is new in the Collection interface and works much like a built-in iterator (we will see in more detail ahead). The stream prepares the results of the collection, which then passes to the filter method, describing "how" the parties will be filtered using the lambda expression that compares year undergraduate students to 2011.

Note that there is an explicit return. Simply say "Compare the year of graduation in 2011" and the compiler infers that intended to Predicate interface (which has a single method that requires a signature with a return of type boolean). The map method is processed similarly using a lambda expression by passing a student S as a parameter and mapping (as a translator) its return value, which is the score (score) of the student. The method map should not be confused with the java.util.Map that uses key-value pairs. The Stream class map method returns a new instance of Stream containing the results of the operation applied to all input Stream elements, producing, in this case, a Stream with all the notes.

Using lambdas implement the same algorithm with much less code. It is understandable therefore less error prone, and as seen, it can be changed to a parallel algorithm since there is no sharing state.

It is useful to look at the implementation of decisions that language designers made to develop lambdas.

Looking for Java as a whole, there are many interfaces that have only one method.

Let us define a functional interface as an interface with exactly one abstract method, for example:

Listing 1. Defining functional interface.

Comparator <T> interface {boolean compare (T x, T y); }
FileFilter interface {boolean accept (File x); }
Runnable interface {void run (); }
ActionListener interface {void actionPerformed (...); }
Callable <T> {T call () interface; }

A lambda expression allows you to define a functional interface (again, an abstract method) that the compiler identifies the structure. The compiler can determine the functional interface represented from its position. The type of a lambda expression is the associated functional interface.

Because the compiler knows the locations that are used a lambda expression, you can determine a lot about this expression. So as the compiler knows the type of functional interface, so you can infer the other needed.

Reference methods

The syntax of the reference method is another new feature of the lambda expression. It is a shortcut that allows you to reuse a method basically as a lambda expression. We can do things like:

 FileFilter x = f -> f.canRead();

This syntax tells it to create a FileFilter that filters files based on a common property - in this case, if the file can be read. Note that in this example, we never mentioned that f is a file; the compiler infers by signing the only method in FileFilter:

boolean accept(File pathname);

It can be further simplified using the new notation Java 8 "::".

 FileFilter x = File::canRead;
These syntaxes are completely equivalent. To call a constructor analogous manner, the syntax can be used "::new". For example, if we have a functional interface as:
interface Factory<T> {
 T make();
}

So we can say:

 Factory<List<String>> f = ArrayList<String>::new;

This is equivalent to:

 Factory<List<String>> f = () -> return new ArrayList<String>();

And now, when f.make() is called, it returns a new ArrayList<String>.

Using functional interfaces, the compiler can infer many things about typing and intention, as shown in these examples.

Library Evolution

One advantage of lambdas and expression codes as data is that, as seen, existing libraries have been updated to accept lambdas as parameters. This introduces some complexity: how to introduce methods in the interface without breaking the implementations of interfaces that already exist?

To do this, the Java introduces the concept of extension methods, also known as defending methods or methods default (default).

We will explain using an example. The stream method was added to the Collection interface to provide basic support to lambda. To add the stream method in the interface without breaking existing implementations of Collection worldwide, Java added the stream as a method of interface, providing a default implementation:

 interface Collection<E> {
default Stream<E> stream() { return StreamSupport.stream(spliterator()); } }

So now we have the option of implementing the stream method or prefer to use the default implementation provided by Java.

Aggregation operations

Business operations often involve aggregations as: finding the sum, maximum, or average of a data set, or group of something. So far, these operations are normally executed with external interaction loops, as we said, in restricting the optimizations and adding boilerplate to the source code.

The answer is that potentially will not be finalized. You can easily make a code snippet using streams that continues forever, like a loop "while (true);" infinite. It is like a Stream: If you use an endless stream, it can never end. But it is also possible to make a Stream stop - say, to provide an endless stream of random numbers, but with a stopping point. So the Stream will stop and the program can continue execution.

Stream provides a pipeline (sequence) data with three important components:

1. A data source;

2. Zero or more intermediate operations, providing a pipeline streams;

3. An end operation (end) that can perform two functions: Create a result or side effect. (A side effect means that may be unable to return a result, but instead, can print the value.)

In this example, we started with a Collection of "transactions" and we want to determine the total price of all transactions that were made by buyers of London. We get a stream from the transaction Collection.

After we get a stream from the transaction Collection. Then, we apply a filter operation to produce a new Stream with buyers in London.

Next, we apply the intermediate operation to extract mapToInt prices. Finally, we apply the final sum operation to obtain the result.

From the point of view of implementation, what happened here is that the filter and the map method (the intermediate operation) did not make a computer work. They were only responsible for setting up all the operations, and the actual computing is performed later, postponed to call the final operation - in this case the sum (sum) - that makes all the work happens.

Stream sources

There are several ways to get a Stream. Many methods are being added to Collection API (using extension methods in interfaces).

Through a List, Set, or Map.Entry you can call a Stream method that returns a Stream with the contents of the collection.

An example is the stream() method, or parallelStream(), which house the library uses the fork/join framework to decompose the actions in various subtasks.

There are other ways to get a stream:

  • Provide an array to the stream() method of the Array class;
  • Call the Stream.of() method, a static method of the Stream class;
  • Calling a new static methods to return a particular stream, for example:

o IntStream.range(), supplying a start index and an end. For example, IntStream.range(1, 10) generates a stream 1 to 9 with an increment of 1 on 1 (IntRange.rangeClosed (1, 10) generates a stream of 1 to 10);

o Files.walk() with a path and a parameter optional control that returns a stream of individual subdirectories or files;

o Implement java.util.Spliterator interface to define your own way to create a Stream. For more information about the Spliterator see the Javadoc SE 8 provided by Oracle.

Stream finishing operations

After encadearmos all of these streams, can specify a finishing operation to perform pipelined and all transactions (sequentially or in parallel) and produce the end results (or side effects).

Listing 2. Final result operation.

int sum = transactions.stream().
    filter(t -> t.getBuyer().getCity().equals("London")). //Lazy
    mapToInt(Transaction::getPrice). //Lazy
    sum(); //run pipeline

Inteface Iterable

This is an old acquaintance from the days of Java 1.5, except that now has a forEach() method that accepts a Consumer, which accepts a single argument and returns no value and produces a side effect. But it remains an external interaction and the best way to get a lambda is the map() method.

Examples

Now we'll see some useful examples, which are listed below with explanatory comments. (The bold lines indicate the special use shown in each example).

Example 1. Convert words to uppercase:

List<String> output = wordList.stream().
  // Map the entire String list in capital letters.
  map(String::toUpperCase).
  // Converts the stream to a list.
  collect(Collectors.toList());

Example 2. Find the words with pocket size from the list:

List<String> output = wordList.stream().
 //Select only the words with pocket size.
  filter(w -> (w.length() & 1 == 0). 
 collect(Collectors.toList());

Example 3. Counting the lines of a file:

long count = bufferedReader.
  // Get a stream of individual lines. This is the new method
  // bufferedReader that returns a stream<string>.
  lines(). 
  // Account the input stream elements.
  count();

Example 4. Add the lines 3 and 4 in a single String:

String output = bufferedReader.lines().
  // skip first two lines.
  skip(2).
  // Limits the stream just the next two lines.
  limit(2).
  // Concatenate lines.
  collect(Collectors.joining());

Example 5. Find the size of the longest line in a file:

int longest = reader.lines().
  mapToInt(String::length).
 // Creates a new stream with the size of the mapping Strings
  // The current string to the corresponding size.
  max().
  // Collection the largest size stream element (such as a OptionalInt).
  getAsInt();
  // Update the OptionalInt with an int.

Example 6. Collection of all file of words in a list:

  List<String> output = reader.lines().
  flatMap(line -> Stream.of(line.split(REGEXP))). 
   // Receive a stream of words
   // All lines.
  filter(word -> word.length() > 0).
   // Filter empty Strings.
  collect(Collectors.toList());
   // Create the return list.

Example 7. Returns the list of tiny words in alphabetical order:

 List<String> output = reader.lines().
  flatMap(line -> Stream.of(line.split(REGEXP))).
  filter(word -> word.length() > 0).
  map(String::toLowerCase).
   // Update the Stream source with Stream
   // small letters.
  sorted().
  // Update the stream with the version ordered.
  collect(Collectors.toList());
  // Creates and returns a list

Conclusion

Simon Ritter concluded the presentation by stating:

"The Java needs of lambda expressions to make life easier for the developer. The lambdas expressions were necessary for the creation of Streams and also to implement the idea of behavior of passage as the passage value. We also needed to extend existing interfaces, using extensions methods Java SE 8, and it solves the problem of backwards compatibility. This allows providing the idea of batch operations in Collections and allows things that are simpler and more readable way. Java SE 8 is basically evolving language, evolving class libraries and also virtual machines at the same time. "

Java 8 is available for download and there is good support for lambda in all major IDEs. I suggest that all Java developers to download and use the Lambda Project.



Web developer and passioned for web design, SEO and front end technologies.

What did you think of this post?
Services
[Close]
To have full access to this post (or download the associated files) you must have MrBool Credits.

  See the prices for this post in Mr.Bool Credits System below:

Individually – in this case the price for this post is US$ 0,00 (Buy it now)
in this case you will buy only this video by paying the full price with no discount.

Package of 10 credits - in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download few videos. In this plan you will receive a discount of 50% in each video. Subscribe for this package!

Package of 50 credits – in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download several videos. In this plan you will receive a discount of 83% in each video. Subscribe for this package!


> More info about MrBool Credits
[Close]
You must be logged to download.

Click here to login