Introduction to the

Lambda expressions are an important new feature in Java SE 8. Lambda expressions allow you to replace functional interfaces with expressions. Lambda expressions, like methods, provide a normal list of arguments and a body (which can be an expression or a block of code) that uses those arguments.

Lambda expressions also enhance the collection library. Java SE 8 adds two packages for batch manipulation of collection data: the java.util.function package and the java.util.stream package. A stream is like an iterator, but with a lot of extra functionality. Collectively,lambda expressions and streams are the biggest changes to the Java language since Generics and annotations were added. In this article, we’ll look at the power of lambda expressions and streams from simple to complex examples.

Environment to prepare

If you haven’t already installed Java 8, you should install lambda and Stream before you can use them. Tools and ides like NetBeans and IntelliJ IDEA support Java 8 features, including lambda expressions, repeatable annotations, compact profiles, and other features.

Here are the download links for Java SE 8 and IntelliJ IDEA 2016:

Java Platform (JDK 8): Download Java 8 from Oracle or with NetBeans IDE

IntelliJ IDEA 2016: Download IntelliJ IDEA from IntelliJ IDEA official website.

Syntax for Lambda expressions

Basic syntax:

(parameters) -> expression 或 (parameters) ->{ statements; }

Here is a simple example of a Java lambda expression:

// 1. No arguments, return value 5 () -> 5 // 2. Take an argument (numeric type) and return twice its value x -> 2 * x // 3. Take two arguments (numbers) and return their difference (x, y) -> x -- y // 4. Accept 2 int integers and return their sum (int x, int y) -> x + y Take a string object and print it on the console without returning any value (looks like returning void) (string s) -> system.out.print (s)Copy the code

Basic Lambda example

Now that we know what a lambda expression is, let’s start with some basic examples. In this section, we’ll see how lambda expressions affect the way we code. Given a List of players, programmers can iterate through it using a for statement (“for loop “), which in Java SE 8 can be converted to another form:

String[] atp = {"Rafael Nadal", "Novak Djokovic", "Stanislas Wawrinka", "David Ferrer","Roger Federer", "Andy Murray","Tomas Berdych", "Juan Martin Del Potro"}; List<String> players = Arrays.asList(atp); For (String player: players) {system.out.print (player + "; "); } player. forEach((player) -> system.out. print(player + "; ")); // In Java 8, use the double colon operator players.forEach(system. out::println);Copy the code

As you can see,lambda expressions can reduce our code to a single line. Another example is in graphical user interface programs where anonymous classes can be replaced with lambda expressions. Similarly, when implementing the Runnable interface, you can use it like this:

// Use the anonymous inner class btn.setonAction (new EventHandler<ActionEvent>() {@override public void handle(ActionEvent event) { System.out.println("Hello World!" ); }}); // Or use lambda expression btn.setonAction (event -> system.out. println("Hello World!") ));Copy the code

Here is an example of using lambdas to implement the Runnable interface:

Public void run() {system.out.println ("Hello world!"); ); } }).start(); Lambda expression new Thread(() -> system.out.println ("Hello world!") )).start(); {@override public void run() {system.out.println ("Hello world! ); }}; Lambda expression Runnable race2 = () -> system.out.println ("Hello world!") ); // Call the run method directly. race1.run(); race2.run();Copy the code

Lambda expressions of Runnable, using block format, convert five lines of code into a single line statement. Next, in the next section we’ll use lambdas to sort collections.

Use Lambdas to sort collections

In Java, the Comparator class is used to sort collections. In the following example, we will look at the player’s name, surname, name length and the last letter. As in the previous example, we first use anonymous inner classes to sort, and then use lambda expressions to simplify our code.

In the first example, we will sort the list by name. In the old way, the code looks like this:

String[] players = {"Rafael Nadal", "Novak Djokovic", "Stanislas Wawrinka", "David Ferrer", "Roger Federer", "Andy Murray", "Tomas Berdych", "Juan Martin Del Potro", "Richard Gasquet", "John Isner"}; // 1.1 Uses anonymous inner classes to sort players Arrays by name. Sort (players, new Comparator<String>() { @Override public int compare(String s1, String s2) { return (s1.compareTo(s2)); }});Copy the code

Using lambdas, you can do the same with the following code:

SortByName = (String s1, String s2) -> (s1.compareTo(s2)); Arrays.sort(players, sortByName); Sort (players, (String s1, String s2) -> (s1.compareTo(s2)));Copy the code

The rest are sorted as follows. As in the previous example, the code implements the Comparator using anonymous inner classes and lambda expressions, respectively:

// Sort players Arrays using anonymous inner class surname. Sort (players, new Comparator<String>() { @Override public int compare(String s1, String s2) { return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" ")))); }}); SortBySurname = (String s1, String s2) -> ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) ); Arrays.sort(players, sortBySurname); // if the author is wrong, there are so many parentheses... Arrays.sort(players, (String s1, String s2) -> ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) ) ); // 2.1 Use anonymous inner class to sort players Arrays by name lenght. Sort (players, new Comparator<String>() { @Override public int compare(String s1, String s2) { return (s1.length() - s2.length()); }}); <String> sortByNameLenght = (String s1, String s2) -> (s1.length() - s2.length()); Arrays.sort(players, sortByNameLenght); Or this Arrays. Sort (players, (String s1, String s2) -> (s1.length() -s2.length ())); // Arrays uses anonymous inner classes to sort players based on the last letter. Sort (players, new Comparator<String>() { @Override public int compare(String s1, String s2) { return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)); }}); SortByLastLetter = (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)); Arrays.sort(players, sortByLastLetter); Or this Arrays. Sort (players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)));Copy the code

That’s it, simple and intuitive. In the next section we’ll explore more lambdas’ capabilities and use them in conjunction with streams.

Use Lambdas and Streams

Stream is a wrapper around a collection and is often used with lambda. Many operations can be supported using lambdas, such as map, filter, limit, sorted, count, min, Max, sum, collect, and so on. Also, streams use lazy arithmetic, they don’t actually read all the data, and they end up with chained syntax when they encounter methods like getFirst(). In the following examples, we’ll explore what Lambdas and Streams can do. We create a Person class and use it to add some data to the list for further flow operations. Person is just a simple POJO class:

public class Person {

private String firstName, lastName, job, gender;
private int salary, age;

public Person(String firstName, String lastName, String job,
                String gender, int age, int salary)       {
          this.firstName = firstName;
          this.lastName = lastName;
          this.gender = gender;
          this.age = age;
          this.job = job;
          this.salary = salary;
}
// Getter and Setter 
// . . . . .
}
Copy the code

Next, we’ll create two lists, both of which will hold the Person object:

List<Person> javaProgrammers = new ArrayList<Person>() { { add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 43, 2000)); add(new Person("Tamsen", "Brittany", "Java programmer", "female", 23, 1500)); add(new Person("Floyd", "Donny", "Java programmer", "male", 33, 1800)); add(new Person("Sindy", "Jonie", "Java programmer", "female", 32, 1600)); add(new Person("Vere", "Hervey", "Java programmer", "male", 22, 1200)); add(new Person("Maude", "Jaimie", "Java programmer", "female", 27, 1900)); add(new Person("Shawn", "Randall", "Java programmer", "male", 30, 2300)); add(new Person("Jayden", "Corrina", "Java programmer", "female", 35, 1700)); add(new Person("Palmer", "Dene", "Java programmer", "male", 33, 2000)); add(new Person("Addison", "Pam", "Java programmer", "female", 34, 1300)); }}; List<Person> phpProgrammers = new ArrayList<Person>() { { add(new Person("Jarrod", "Pace", "PHP programmer", "male", 34, 1550)); add(new Person("Clarette", "Cicely", "PHP programmer", "female", 23, 1200)); add(new Person("Victor", "Channing", "PHP programmer", "male", 32, 1600)); add(new Person("Tori", "Sheryl", "PHP programmer", "female", 21, 1000)); add(new Person("Osborne", "Shad", "PHP programmer", "male", 32, 1100)); add(new Person("Rosalind", "Layla", "PHP programmer", "female", 25, 1300)); add(new Person("Fraser", "Hewie", "PHP programmer", "male", 36, 1100)); add(new Person("Quinn", "Tamara", "PHP programmer", "female", 21, 1000)); add(new Person("Alvin", "Lance", "PHP programmer", "male", 38, 1600)); add(new Person("Evonne", "Shari", "PHP programmer", "female", 40, 1800)); }};Copy the code

Now we use the forEach method to iterate over the above list:

System.out.println(" Names of all programmers :"); javaProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName())); phpProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));Copy the code

We also use forEach to increase programmer salaries by 5%:

System.out.println(" give programmers a 5% raise :"); Consumer<Person> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary()); javaProgrammers.forEach(giveRaise); phpProgrammers.forEach(giveRaise);Copy the code

Another useful method is filter(), which lets us show PHP programmers who earn more than $1400 per month:

PhpProgrammers.stream().filter((p) -> (p.get_salary ())); .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));Copy the code

We can also define filters and then reuse them to perform other operations:

Predicate<Person> ageFilter = (p) -> (p.gage () > 25); // Define filters <Person> ageFilter = (p) -> (p.gage () > 25); Predicate<Person> salaryFilter = (p) -> (p.getSalary() > 1400); Predicate<Person> genderFilter = (p) -> ("female".equals(p.getGender())); System.out.println(" Below are female PHP programmers over 24 years old with a monthly salary of $1,400 or more :"); phpProgrammers.stream() .filter(ageFilter) .filter(salaryFilter) .filter(genderFilter) .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName())); // reuse filters system.out.println (" Java programmers: older than 24 "); javaProgrammers.stream() .filter(ageFilter) .filter(genderFilter) .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));Copy the code

Using the limit method, you can limit the number of result sets:

System.out.println(" the first 3 Java programmers:"); javaProgrammers.stream() .limit(3) .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName())); System.out.println(" top 3 female Java programmers:"); javaProgrammers.stream() .filter(genderFilter) .limit(3) .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));Copy the code

The sorting? Can we handle it in stream? The answer is yes. In the following example, we will sort Java programmers by name and salary, put them into a list, and display the list:

System.out.println(" sort by name and show the first 5 Java programmers:"); List<Person> sortedJavaProgrammers = javaProgrammers .stream() .sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName()))) .limit(5) .collect(toList()); sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName())); System.out.println(" Java programmers:"); sortedJavaProgrammers = javaProgrammers .stream() .sorted( (p, p2) -> (p.getSalary() - p2.getSalary()) ) .collect( toList() ); sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));Copy the code

If we are only interested in the lowest and highest salaries, faster than sorting the first/last is the min and Max method:

System.out.println(" Java programmer with the lowest pay :"); Person pers = javaProgrammers .stream() .min((p1, p2) -> (p1.getSalary() - p2.getSalary())) .get() System.out.printf("Name: %s %s; Salary: $%,d.", pers.getFirstName(), pers.getLastName(), pers.getsalary ()) system.out.println (" highest paid Java programmer:"); Person person = javaProgrammers .stream() .max((p, p2) -> (p.getSalary() - p2.getSalary())) .get() System.out.printf("Name: %s %s; Salary: $%,d.", person.getFirstName(), person.getLastName(), person.getSalary())Copy the code

We have seen how the Collect method works in the above example. In combination with the map method, we can use the Collect method to put our result Set into a string, a Set, or a TreeSet:

System.out.println(" code code for PHP programmers' first name as string :"); String phpDevelopers = phpProgrammers .stream() .map(Person::getFirstName) .collect(joining(" ; ")); // System.out.println(" code for Java programmers' first name in Set:"); Set<String> javaDevFirstName = javaProgrammers .stream() .map(Person::getFirstName) .collect(toSet()); System.out.println(" Code for Java programmers' first name on TreeSet:"); TreeSet<String> javaDevLastName = javaProgrammers .stream() .map(Person::getLastName) .collect(toCollection(TreeSet::new));Copy the code

Streams can also be parallel. The following is an example:

System.out.println(" Calculate the code that pays the Java programmers all the money:"); int totalSalary = javaProgrammers .parallelStream() .mapToInt(p -> p.getSalary()) .sum();Copy the code

You can use the summaryStatistics method to get various summary data for elements in the Stream. Next, we can access methods such as getMax, getMin, getSum, or getAverage:

// Calculate count, min, Max, sum, and average for numbers List<Integer> numbers = Arrays. AsList (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); IntSummaryStatistics stats = numbers .stream() .mapToInt((x) -> x) .summaryStatistics(); System.out.println(" maximum number in List: "+ stats.getmax ()); System.out.println(" minimum number in List: "+ stats.getmin ()); System.out.println(" sum of all numbers: "+ stats.getsum ()); System.out.println(" Average of all numbers: "+ stats.getaverage ());Copy the code

OK, that’s it, I hope you like it!

conclusion

In this article, we learned different ways to use lambda expressions, from basic examples to complex examples using Lambdas and Streams. In addition, you learned how to sort Java collections using lambda expressions and the Comparator class.