开始使用Java Lambda表达式

Tags: java

简介(企业案例)

Lambda表达式是包含在Java SE8的一个新的重要功能,lambda表达式可以使用一个表达式来表示一个方法。一个lambda表达式很像method,它提供了参数和主体(可以是一个表达式或代码块)。
Lambda表达式也提高了Collection库。Java SE8添加了java.util.function和java.util.stream两个包用于批量处理Collections的数据。一个流就像是一个迭代器,但有许多额外的功能。结合两者,lambda表达式和streams是自从泛型和注解以来添加到java语言的最大变化。在本文中,我们将通过从简单到复杂的例子来探讨lambda表达式和streams的功能。

先决条件

如果你还没有安装Java 8,那么这就是你在使用lambdas和streams前所应该做的第一件事。NetBeans和IntelliJ IDEA均支持Java 8的新特性,如lambda表达式,、可重复的注解、紧凑的配置文件等。
下面Java SE 8和NetBeans IDE 8的下载链接:

Lambda表达式语法

一个lambda的基本语法可以是:

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

Java的lambda表达式举例如下:

1.  () -> 5           // 没有参数,返回5
2.  x -> 2 * x        // 取得数字类型参数x,返回2*x
3.  (x, y) -> x – y    // 获取2个数字类型参数x,y,返回二者的差
4.  (int x, int y) -> x + y     // 获取2个整数类型参数x,y,返回二者和
5.  (String s) -> System.out.print(s)    // 获取字符串参数,打印

基本lambda表达式的例子

现在,我们知道lambda表达式是什么了,让我们先从一些基本的例子开始。在本节中,我们将看到Lambda表达式如何影响我们编码的方式。假设我们有一个球员清单需要使用for循环遍历,对于程序员来说通常指的是for语句,在Java SE8中我们可以这样编写代码:

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 + "; ");
}
       
// 使用lambda表达式
players.forEach((player) -> System.out.print(player + "; "));
 
// 使用Java 8双分好操作符
players.forEach(System.out::println);

正如你所看到的,lambda表达式可以在我们的代码减少一行。另一个例子是一个图形用户界面的应用程序,匿名类可以被替换为lambda表达式。当实现Runnable接口时会发生同样的事情:

// 使用匿名内部类
btn.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
        System.out.println("Hello World!"); 
            }
 });
 
// 使用lambda表达式
btn.setOnAction(event -> System.out.println("Hello World!"));

下面代码显示如何使用lambda表达式实现Runnable接口

//使用匿名内部类
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello world !");
    }
}).start();
 
// 使用lambda表达式
new Thread(() -> System.out.println("Hello world !")).start();
 
// 使用匿名内部类
Runnable race1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello world !");
    }
};
 
// 使用lambda表达式
Runnable race2 = () -> System.out.println("Hello world !");
 
// 执行
race1.run();
race2.run();

可以看到使用lambda实现的Runnable将代码缩减为一行。更进一步,在下一节中我们将使用lambdas排序集合。

使用lambda对集合进行排序

在Java中,Comparator类是用于对集合进行排序。在下面的例子中,我们将基于球员的name, surname, name的长度以及last name字符对球员进行排序. 。我们将首先使用匿名内部类排序,然后使用lambda表达式减少我们的代码。
在第一个例子中,我们将针对名字进行排序。使用老方法,这看起来是这样的:

String[] players = {"Rafael Nadal", "Novak Djokovic", 
             "Stanislas Wawrinka", "David Ferrer", 
             "Roger Federer", "Andy Murray", 
             "Tomas Berdych", "Juan Martin Del Potro", 
             "Richard Gasquet", "John Isner"};
 
// 根据名字对球员进行排序,使用内部类
Arrays.sort(players, new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return (s1.compareTo(s2));
    }
});

使用lambda表达式,同样的事情可以这样完成:

// 使用lambda表达式排序
Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));
 
Arrays.sort(players, sortByName);
// 或者这样
Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2)));

其余排序列举如下。同上面的例子一样,代码应用Comparator使用匿名内部类和相对应的lambda表达式:

// 根据surname对球员进行排序,使用匿名内部类
Arrays.sort(players, new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" "))));
    }
});
 
// 根据surname对球员进行排序l,使用ambda表达式
Comparator<String> sortBySurname = (String s1, String s2) -> (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" "))));
 
Arrays.sort(players, sortBySurname);
// 或这样
Arrays.sort(players, (String s1, String s2) -> (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" ")))));
 
// 根据name长度对球员进行排序,使用匿名内部类
Arrays.sort(players, new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return (s1.length() - s2.length());
    }
});
 
// 根据name长度对球员进行排序,使用lambda表达式
Comparator<String> sortByNameLenght = (String s1, String s2) -> (s1.length() - s2.length());
 
Arrays.sort(players, sortByNameLenght);
// 或这样
Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length()));
 
// 根据last letter对球员进行排序。使用匿名内部类
Arrays.sort(players, new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
    }
});
 
// 根据last letter对球员进行排序。使用lambda表达式
Comparator<String> 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)));

这就是全部代码,非常简单。在下一节中,我们将结合lambda和stream探讨更多的lambda表达式功能。

使用lambda表达式和stream

Streams是Collections的wrapper,他无时无刻都在使用lambda表达式。他们支持使用lambda表达式完成多种操作,如map,filter,limit,sorted,count,min,max,sum,collect等。此外,Streams是延迟加载的,他们实际上并没有读取所有的数据,如方法getFirst()可以结束流。在下面的例子中,我们将探讨lambda表达式和Streams可以做到什么。我们创建了一个Person类,并使用这个类向list添加一些数据,这些数据将在后续的stream操作中使用。 Person类只是一个简单的POJO:

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 
. . . . .   
}

然后,我们将创建两个包含Person对象的list:

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));
      }
};

现在让我们用forEach方法来遍历上面的list:

System.out.println("Show programmers names:");
javaProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
phpProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

我们将使用同样的forEach方法尝试将每个程序猿的薪水提高5%:

System.out.println("Increase salary by 5% to programmers:");
Consumer<Person> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());
 
javaProgrammers.forEach(giveRaise);
phpProgrammers.forEach(giveRaise);

另一个有用的方法是filter,我们可以使用这个方法显示所有收入超过1400元的PHP程序猿:

System.out.println("Show PHP programmers that earn more than $1,400:")
phpProgrammers.stream()
          .filter((p) -> (p.getSalary() > 1400))
          .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

我们同样可以定义一个filter,并在后面的代码中使用它:

// Define some filters
Predicate<Person> ageFilter = (p) -> (p.getAge() > 25);
Predicate<Person> salaryFilter = (p) -> (p.getSalary() > 1400);
Predicate<Person> genderFilter = (p) -> ("female".equals(p.getGender()));
 
System.out.println("Show female PHP programmers that earn more than $1,400 and are older than 24 years:");
phpProgrammers.stream()
          .filter(ageFilter)
          .filter(salaryFilter)
          .filter(genderFilter)
          .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
 
// Reuse filters
System.out.println("Show female Java programmers older than 24 years:");
javaProgrammers.stream()
          .filter(ageFilter)
          .filter(genderFilter)
          .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()))

结果可以限制条数,如果我们用limit的方法:

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

怎么排序呢?我们可以通过stream做到这一点吗?答案是肯定的,我们能做到。在下面的例子中,我们将整理Java程序员的名字和薪水,收集到一个列表然后显示:

System.out.println("Sort and show the first 5 Java programmers by name:");
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("Sort and show Java programmers by salary:");
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()));

如果我们仅兴趣最低和最高工资,比排序更快的方法是通过min和max方法选择第一个(或最后一个)程序猿:

System.out.println("Get the lowest Java programmer salary:");
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("Get the highest Java programmer salary:");
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())

我们通过上面的例子看到了collect方法如何工作的例子。结合map方法,我们可以使用collect方法来收集我们的结果到一个String、Set或TreeSet中:

System.out.println("Get PHP programmers first name to String:");
String phpDevelopers = phpProgrammers
          .stream()
          .map(Person::getFirstName)
          .collect(joining(" ; "));    // this can be used as a token in further operations
 
System.out.println("Get Java programmers first name to Set:");
Set<String> javaDevFirstName = javaProgrammers
          .stream()
          .map(Person::getFirstName)
          .collect(toSet());
 
System.out.println("Get Java programmers last name to TreeSet:");
TreeSet<String> javaDevLastName = javaProgrammers
          .stream()
          .map(Person::getLastName)
          .collect(toCollection(TreeSet::new));

 Steam也可以是串行的。下面是一个例子:

System.out.println("Calculate total money spent for paying Java programmers:");
int totalSalary = javaProgrammers
          .parallelStream()
          .mapToInt(p -> p.getSalary())
          .sum();

要获得不同元素的汇总数据我们可以使用summaryStatistics方法的。展望进一步,我们可以使用像GetMax,getMin,getSum或getAverage这样的方法:

//Get 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("Highest number in List : " + stats.getMax());
System.out.println("Lowest number in List : " + stats.getMin());
System.out.println("Sum of all numbers : " + stats.getSum());
System.out.println("Average of all numbers : " + stats.getAverage());

这就是全部了,希望你喜欢这篇文章。

本文链接:http://www.4byte.cn/learning/120054/kai-shi-shi-yong-java-lambda-biao-da-shi.html