Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explanation of this Lambda Expression

I am creating a Word Comparison class and it will count the occurrences of words as well. (This is Java)

This was my original method:

/**
 * @param map The map of words to search
 * @param num The number of words you want printed
 * @return list of words
 */
public static List<String> findMaxOccurrence(Map<String, Integer> map, int num) {
    List<WordComparable> l = new ArrayList<>();
    for (Map.Entry<String, Integer> entry : map.entrySet())
        l.add(new WordComparable(entry.getKey(), entry.getValue()));

My IDE suggested that the loop and list assignment could be replaced with a "collect call": "stream api calls"

In which it generated this code:

    List<WordComparable> l =
            map.entrySet().stream()
                    .map(entry -> new WordComparable
                            (entry.getKey(), entry.getValue())).collect(Collectors.toList());

I am kinda confused on how the lambda math works. If my memory serves correctly, the -> is the for each loop, but the other calls are completely confusing.

My IDE can also expand the code into these two snippets:

    List<WordComparable> l =
            map.entrySet().stream()
                    .map(entry -> {
                        return new WordComparable
                                (entry.getKey(), entry.getValue());
                    }).collect(Collectors.toList());

And

    List<WordComparable> l =
            map.entrySet().stream()
                    .map(new Function<Map.Entry<String, Integer>, WordComparable>() {
                        @Override
                        public WordComparable apply(Map.Entry<String, Integer> entry) {
                            return new WordComparable
                                    (entry.getKey(), entry.getValue());
                        }
                    }).collect(Collectors.toList());

Any light-shedding would be awesome.

like image 750
Soulevoker Avatar asked Aug 24 '15 19:08

Soulevoker


1 Answers

Let's take a look at the for loop a bit closer to see how we can write it functionally:

List<WordComparable> l = new ArrayList<>();
for (Map.Entry<String, Integer> entry : map.entrySet())
    l.add(new WordComparable(entry.getKey(), entry.getValue()));

If we read that code in plain English, we might say "for each entry of my map, let's convert it to a WordComparable and add it to a list".

Now, we can rephrase that sentence to "for each entry of my map, let's convert it to a WordComparable, and when we have converted it all, let's make a list out of it".

Using that sentence, we see that we need to create a function: one that takes an entry of the map and converts it to a WordComparable. So let's build one! Java 8 introduces a new type named Function, which has one important method: apply. This method takes one input, transforms it and returns one output.

Writing good old Java, since Function is an interface, we can implement it to write our conversion code:

public class EntryConverter implements Function<Map.Entry<String, Integer>, WordComparable> {

    public WordComparable apply(Map.Entry<String, Integer> entry) {
        return new WordComparable(entry.getKey(), entry.getValue());
    }

}

Now that we have this converter, we need to use it on all the entries. Java 8 also introduces the notion of Stream, that is to say, a sequence of elements (note that this sequence can be infinite). Using this sequence, we can finally write into code what we said earlier, i.e. "for each entry, let's convert it to a WordComparable". We make use of the map method, whose goal is to apply a method on each element of the stream.

We have the method: EntryConverter, and we build a Stream of our entries using the stream method.

So, we get:

map.entrySet().stream().map(new EntryConverter());

What remains is the last part of the sentence: "make a List out of it", i.e. collect all the elements into a List. This is done using the collect method. This method takes a Collector as argument, i.e. an object capable of reducing a stream into a final container. Java 8 comes with a lot of prebuilt collectors; one of them being Collectors.toList().

Finally, we get:

map.entrySet().stream().map(new EntryConverter()).collect(Collectors.toList());

Now, if we remove the temporary class EntryConverter and make it anonymous, we get what your IDE is proposing:

List<WordComparable> l = map.entrySet()
                            .stream() //make a Stream of our entries
                            .map(new Function<Map.Entry<String, Integer>, WordComparable>() {
                                 @Override
                                 public WordComparable apply(Map.Entry<String, Integer> entry) {
                                     return new WordComparable(entry.getKey(), entry.getValue());
                                 }
                             }) //let's convert each entry to a WordComparable
                             .collect(Collectors.toList()); //and make a List out of it

Now, writing all that code is a bit cumbersome, especially the declaration of the anonymous class. Java 8 comes to the rescue with the new -> operator. This operator allows the creation of a Function much more painlessly than before: the left side corresponds to the argument of the function and the right side corresponds to the result. This is called a lambda expression.

In our case, we get:

entry -> new WordComparable(entry.getKey(), entry.getValue())

It is also possible to write this lambda expression using a block body and a return statement:

entry -> {
    return new WordComparable(entry.getKey(), entry.getValue());
}

Notice how that corresponds to what we had written earlier in EntryConverter.

This means we can refactor our code to:

List<WordComparable> l = map.entrySet()
                            .stream()
                            .map(entry -> new WordComparable(entry.getKey(), entry.getValue()))
                            .collect(Collectors.toList());

which is much more readable, and is what your IDE proposes.

You can find more about lambda expressions on Oracle site.

like image 142
Tunaki Avatar answered Sep 20 '22 12:09

Tunaki