英语原文的链接在最底下的“阅读原文”

简介

(译者认为: 超过 3 行的逻辑就不适用 Lambda 表达式了。虽然看着很先进,其实 Lambda 表达式的本质只是一个 " 语法糖 ", 由编译器推断并帮你转换包装为常规的代码, 因此你可以使用更少的代码来实现同样的功能。本人建议不要乱用, 因为这就和某些很高级的黑客写的代码一样, 简洁, 难懂, 难以调试, 维护人员想骂娘。谢谢!)

Lambda 表达式是 Java SE 8 中一个重要的新特性。lambda 表达式允许你通过表达式来代替功能接口。lambda 表达式就和方法一样, 它提供了一个正常的参数列表和一个使用这些参数的主体 (body, 可以是一个表达式或一个代码块)。Lambda 表达式还增强了集合库。

Java SE 8 添加了 2 个对集合数据进行批量操作的包: java.util.function 包以及 java.util.stream 包。流 (stream) 就如同迭代器 (iterator), 但附加了许多额外的功能。总的来说, lambda 表达式和 stream 是自 Java 语言添加泛型(Generics) 和注解 (annotation) 以来最大的变化。在本文中, 我们将从简单到复杂的示例中见识 lambda 表达式和 stream 的强悍。

环境准备

如果还没有安装 Java 8, 那么你应该先安装才能使用 lambda 和 stream(译者建议在虚拟机中安装, 测试使用)。像 NetBeans 和 IntelliJ IDEA 一类的工具和 IDE 就支持 Java 8 特性, 包括 lambda 表达式, 可重复的注解, 紧凑的概要文件和其他特性。下面是 Java SE 8 和 NetBeans IDE 8 的下载链接:

Java Platform (JDK 8) http://www.oracle.com/technetwork/java/javase/downloads/index.html

从 Oracle 下载 Java 8, 也可以和 NetBeans IDE 一起下载 https://netbeans.org/downloads/index.html

Lambda 表达式的语法

基本语法:(parameters) -> expression或(parameters) ->{ statements; }下面是 Java lambda 表达式的简单例子:

// 1. 不需要参数,返回值为 5
() -> 5// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)

基本的 Lambda 例子现在, 我们已经知道什么是 lambda 表达式, 让我们先从一些基本的例子开始。在本节中, 我们将看到 lambda 表达式如何影响我们编码的方式。假设有一个玩家 List , 程序员可以使用 for 语句 ("for 循环") 来遍历, 在 Java SE 8 中可以转换为另一种形式:

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 表达式以及函数操作(functional operation)
players.forEach((player) -> System.out.print(player + "; "));// 在 Java 8 中使用双冒号操作符(double colon operator)
players.forEach(System.out::println);

正如您看到的, lambda 表达式可以将我们的代码缩减到一行。另一个例子是在图形用户界面程序中, 匿名类可以使用 lambda 表达式来代替。同样, 在实现 Runnable 接口时也可以这样使用:

// 使用匿名内部类
btn.setOnAction(new EventHandler<ActionEvent>() {@Overridepublic void handle(ActionEvent event) {System.out.println("Hello World!"); }});// 或者使用 lambda expression
btn.setOnAction(event -> System.out.println("Hello World!"));

下面是使用 lambdas 来实现 Runnable 接口 的示例:

// 1.1使用匿名内部类
new Thread(new Runnable() {@Overridepublic void run() {System.out.println("Hello world !");}
}).start();// 1.2使用 lambda expression
new Thread(() -> System.out.println("Hello world !")).start();// 2.1使用匿名内部类
Runnable race1 = new Runnable() {@Overridepublic void run() {System.out.println("Hello world !");}
};// 2.2使用 lambda expression
Runnable race2 = () -> System.out.println("Hello world !");// 直接调用 run 方法(没开新线程哦!)
race1.run();
race2.run();

Runnable 的 lambda 表达式, 使用块格式, 将五行代码转换成单行语句。接下来, 在下一节中我们将使用 lambdas 对集合进行排序。使用 Lambdas 排序集合在 Java 中, Comparator 类被用来排序集合。在下面的例子中, 我们将根据球员的 name, surname, name 长度 以及最后一个字母。和前面的示例一样, 先使用匿名内部类来排序, 然后再使用 lambda 表达式精简我们的代码。在第一个例子中, 我们将根据 name 来排序 list。使用旧的方式, 代码如下所示:

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 使用匿名内部类根据 name 排序 players
Arrays.sort(players, new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {return (s1.compareTo(s2));}
});

使用 lambdas, 可以通过下面的代码实现同样的功能:

// 1.2 使用 lambda expression 排序 players
Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));
Arrays.sort(players, sortByName);// 1.3 也可以采用如下形式:
Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2)));

其他的排序如下所示。和上面的示例一样, 代码分别通过匿名内部类和一些 lambda 表达式来实现 Comparator :

// 1.1 使用匿名内部类根据 surname 排序 players
Arrays.sort(players, new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" "))));}
});// 1.2 使用 lambda expression 排序,根据 surname
Comparator<String> sortBySurname = (String s1, String s2) -> ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) );
Arrays.sort(players, sortBySurname);// 1.3 或者这样,怀疑原作者是不是想错了,括号好多...
Arrays.sort(players, (String s1, String s2) -> ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) ) );// 2.1 使用匿名内部类根据 name lenght 排序 players
Arrays.sort(players, new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {return (s1.length() - s2.length());}
});// 2.2 使用 lambda expression 排序,根据 name lenght
Comparator<String> sortByNameLenght = (String s1, String s2) -> (s1.length() - s2.length());
Arrays.sort(players, sortByNameLenght);// 2.3 or this
Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length()));// 3.1 使用匿名内部类排序 players, 根据最后一个字母
Arrays.sort(players, new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));}
});// 3.2 使用 lambda expression 排序,根据最后一个字母
Comparator<String> sortByLastLetter = (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
Arrays.sort(players, sortByLastLetter);// 3.3 or this
Arrays.sort(players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)));

就是这样, 简洁又直观。在下一节中我们将探索更多 lambdas 的能力, 并将其与 stream 结合起来使用。使用 Lambdas 和 StreamsStream 是对集合的包装, 通常和 lambda 一起使用。使用 lambdas 可以支持许多操作, 如 map, filter, limit, sorted, count, min, max, sum, collect 等等。同样, Stream 使用懒运算, 他们并不会真正地读取所有数据, 遇到像 getFirst() 这样的方法就会结束链式语法。在接下来的例子中, 我们将探索 lambdas 和 streams 能做什么。我们创建了一个 Person 类并使用这个类来添加一些数据到 list 中, 将用于进一步流操作。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
// . . . . .
}

接下来, 我们将创建两个 list, 都用来存放 Person 对象:

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 方法来迭代输出上述列表:

System.out.println("所有程序员的姓名:");
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("给程序员加薪 5% :");
Consumer<Person> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());javaProgrammers.forEach(giveRaise);
phpProgrammers.forEach(giveRaise);

另一个有用的方法是过滤器 filter() , 让我们显示月薪超过 1400 美元的 PHP 程序员:

System.out.println("下面是月薪超过 $1,400 的PHP程序员:")
phpProgrammers.stream().filter((p) -> (p.getSalary() > 1400)).forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

我们也可以定义过滤器, 然后重用它们来执行其他操作:

// 定义 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("下面是年龄大于 24岁且月薪在$1,400以上的女PHP程序员:");
phpProgrammers.stream().filter(ageFilter).filter(salaryFilter).filter(genderFilter).forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));// 重用filters
System.out.println("年龄大于 24岁的女性 Java programmers:");
javaProgrammers.stream().filter(ageFilter).filter(genderFilter).forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

使用 limit 方法, 可以限制结果集的个数:

System.out.println("最前面的3个 Java programmers:");
javaProgrammers.stream().limit(3).forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));System.out.println("最前面的3个女性 Java programmers:");
javaProgrammers.stream().filter(genderFilter).limit(3).forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

排序呢? 我们在 stream 中能处理吗? 答案是肯定的。在下面的例子中, 我们将根据名字和薪水排序 Java 程序员, 放到一个 list 中, 然后显示列表:

// 静态引入

import static java.util.stream.Collectors.toList;

System.out.println("根据 name 排序,并显示前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("根据 salary 排序 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()));

如果我们只对最低和最高的薪水感兴趣, 比排序后选择第一个 / 最后一个 更快的是 min 和 max 方法:

System.out.println("工资最低的 Java programmer:");
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("工资最高的 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())

上面的例子中我们已经看到 collect 方法是如何工作的。结合 map 方法, 我们可以使用 collect 方法来将我们的结果集放到一个字符串, 一个 Set 或一个 TreeSet 中:

System.out.println("将 PHP programmers 的 first name 拼接成字符串:");
String phpDevelopers = phpProgrammers.stream().map(Person::getFirstName).collect(joining(" ; ")); // 在进一步的操作中可以作为标记(token)   System.out.println("将 Java programmers 的 first name 存放到 Set:");
Set<String> javaDevFirstName = javaProgrammers.stream().map(Person::getFirstName).collect(toSet());System.out.println("将 Java programmers 的 first name 存放到 TreeSet:");
TreeSet<String> javaDevLastName = javaProgrammers.stream().map(Person::getLastName).collect(toCollection(TreeSet::new));

Streams 还可以是并行的 (parallel)。示例如下:

System.out.println("计算付给 Java programmers 的所有money:");
int totalSalary = javaProgrammers.parallelStream().mapToInt(p -> p.getSalary()).sum();

我们可以使用 summaryStatistics 方法获得 stream 中元素的各种汇总数据。接下来, 我们可以访问这些方法, 比如 getMax, getMin, getSum 或 getAverage:

//计算 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("List中最大的数字 : " + stats.getMax());
System.out.println("List中最小的数字 : " + stats.getMin());
System.out.println("所有数字的总和   : " + stats.getSum());
System.out.println("所有数字的平均值 : " + stats.getAverage());

OK, 就这样, 希望你喜欢它!

总结

在本文中, 我们学会了使用 lambda 表达式的不同方式, 从基本的示例, 到使用 lambdas 和 streams 的复杂示例。此外, 我们还学习了如何使用 lambda 表达式与 Comparator 类来对 Java 集合进行排序。


中文翻译者:铁锚

中文翻译文章链接:

https://blog.csdn.net/renfufei/article/details/24600507

英文原文文章链接在底部的“阅读原文”,即可直接阅读全英文的原文版本

关注我!Java从此不迷路!

☟☟☟☟  点击“阅读原文”,即可阅览原汁原味的英文版的技术文章。

☟☟☟☟

☟☟☟☟

☟☟☟☟

【外文翻译】外国友人写得很不错的Java Lambda表达式入门教程,我终于翻译好给大家啦!!!...相关推荐

  1. 阿里的推荐算法竞赛的宣传稿写得很不错,很生动,吸引眼球

    阿里的推荐算法竞赛的宣传稿写得很不错,很生动,吸引眼球.贴在这里,以后可以借鉴 ------------------------------------------------------------ ...

  2. 写给运营同学和初学者的SQL入门教程

    作者简介 多肉,饿了么资深python工程师.曾在17年担任饿了么即时配送众包系统的研发经理,这篇文章最早的版本就诞生于那段时间,目前负责配送相关业务系统的整体稳定性建设.个人比较喜欢c和python ...

  3. 一篇生物学博士的自白,写的很不错,博士生的真实写照

    文章索引: 一.前言 二.读完博士能够干什么? 三.怎样的人适合读博士? 四.怎样读博士? 五.在美国读博士 六.结语--关于事业 正文: 这篇文章说明了男怕入错行. 一.前言 原先我是准备等到毕业的 ...

  4. 一个写得很不错的vuex详解(转)

    https://segmentfault.com/a/1190000015782272?utm_source=tag-newest 转载于:https://www.cnblogs.com/hj0711 ...

  5. 表妹求我写个node脚本,把java错误码表转成excel并翻译成英文

    java原始代码部分如下 public static final int ERR_ONLINE_DECLINED = 1025; //交易联机拒绝//public static final int E ...

  6. 写给Python程序员的PHP快速入门教程

    因为某些原因需要临时接手一个PHP项目,所以决定花点时间学习下PHP,对有其它语言编程经验的人来说来,上手还是很顺的.如果你也刚好在学PHP,希望本文对你有帮助. 安装Laragon 如果你是Wind ...

  7. 会做菜就会编程?一篇写给从未编程过的人的入门教程

    简介: 编程没有那么难,会做菜就会编程. 平时工作之余,很多蚂蚁技术同学也乐于分享技术心得和经验感悟,我们会不定期精选其中的优秀文章,分享给大家. 不少同学对于编程感到好奇,但一看到厚厚的教程就打退堂 ...

  8. Understand Lambda Expressions in 3 minutes(翻译)

    本文翻译自CodeProject上的一篇简单解释Lambda表达式的文章,适合新手理解.译文后面我补充了一点对Lambda表达式的说明. 1.什么是Lambda表达式? Lambda表达式是一种匿名方 ...

  9. 很有用的自定义View详解教程

      注册  登录 转载地址:http://www.jianshu.com/p/c84693096e41 自定义View,有这一篇就够了 字数4899  阅读2302  评论20  喜欢105 我的CS ...

最新文章

  1. 超越RetinaFace,腾讯优图 ASFD 已在 WIDER FACE 霸榜半年!
  2. MPU6050参考代码
  3. no such file or directory AndroidManifest.xml
  4. phoenix Explain Plan 翻译
  5. python常用的绘图库_Python3绘图库Matplotlib(01)
  6. python画多层网络_绘制多层n
  7. matlab repmat 函数的使用
  8. PyTorch学习—15.PyTorch中TensorBoard的使用
  9. 接口自动化测试框架搭建(4、公用方法之url的拼接)--python+HTMLTestRunnerCN+request+unittest+mock+db
  10. 上海工程技术大学c语言商店存货管理系统,商店存货管理系统.docx
  11. 【应用随机过程】01. 随机过程的基本概念
  12. linux将时钟放在桌面上的,天气预报时钟插件加入你的Ubuntu桌面中
  13. mongo数据库之修改器的简单使用
  14. 计算机应用基础2020答案形考,国开2020秋季答案《计算机应用基础(本)》形考学习过程表现...
  15. 球相交的表面积并/体积并
  16. 熬了一夜!我用Python做了一个网站,帮小姐姐生成漫画头像
  17. Cesium实现自定义的广告牌效果
  18. 你知道吗?iPhone耳机旁边的小孔是做什么用的?
  19. 车牌首字母对应省份代码
  20. NLP5:NLTK词性标注

热门文章

  1. matlab scatter 散点图画法
  2. tmux常用命令大全
  3. (转载)简述马尔可夫链
  4. 冲激响应不变法或双线性变换法中的参数T为什么是一个无关紧要的参数
  5. 关于type_info与typeid
  6. 大数据入门及各类技术介绍
  7. Internal error: : 8 [#1] PREEMPT SMP ARM,vmlinux反汇编命令调试查找错误的步骤
  8. linux运行java程序内存过大_排查java应用linux环境内存占用过高的问题
  9. cpp mysql_使用MYSQLCPPCONN连接MYSQL数据库与读写BLOB字段
  10. R语言垃圾邮件分类--朴素贝叶斯(机器学习)