JavaSE 8 :Lambda 快速学习(四) 完结
来源:互联网 发布:淘宝网沙发 编辑:程序博客网 时间:2024/05/05 06:14
Lambda Expressions and Collections
前面的章节已经介绍过Function接口,也已经实现基本的Lambda表达式语法示例.这一章节我们重新回顾Lambda表达式如何通过Collection类来提升.
Lambda Expressions and Collections
到目前所创建的示例中,集合只用到一点.但是一些Lambda表达式新特征改变了集合以往的使用方式.这一章节将介绍这些新特征.
新增类
Drivers,pilots和draftees查询条件都已经被封装到SearchCriteria 类中.
SearchCriteria.java
package com.example.lambda; import java.util.HashMap; import java.util.Map; import java.util.function.Predicate; /** * * @author MikeW */ public class SearchCriteria { private final Map<String, Predicate<Person>> searchMap = new HashMap<>(); private SearchCriteria() { super(); initSearchMap(); } private void initSearchMap() { Predicate<Person> allDrivers = p -> p.getAge() >= 16; Predicate<Person> allDraftees = p -> p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE; Predicate<Person> allPilots = p -> p.getAge() >= 23 && p.getAge() <= 65; searchMap.put("allDrivers", allDrivers); searchMap.put("allDraftees", allDraftees); searchMap.put("allPilots", allPilots); } public Predicate<Person> getCriteria(String PredicateName) { Predicate<Person> target; target = searchMap.get(PredicateName); if (target == null) { System.out.println("Search Criteria not found... "); System.exit(1); } return target; } public static SearchCriteria getInstance() { return new SearchCriteria(); } }
Predicate依赖的查询条件存储在这个类中,对于我们的测试方法也是可以获取到的.
循环
第一个新特性就是对于每个集合类都有一个forEach新方法.下面是打印Person列表的一些示例.
Test01ForEach.java
public class Test01ForEach { public static void main(String[] args) { List<Person> pl = Person.createShortList(); System.out.println("\n=== Western Phone List ==="); pl.forEach( p -> p.printWesternName() ); System.out.println("\n=== Eastern Phone List ==="); pl.forEach(Person::printEasternName); System.out.println("\n=== Custom Phone List ==="); pl.forEach(p -> { System.out.println(p.printCustom(r -> "Name: " + r.getGivenName() + " EMail: " + r.getEmail())); }); } }
第一个例子展示了一个标准的Lambda表达式调用printWesternName方法打印出在列表中的每个人。第二个例子演示的是方法引用,在方法已经存在且表示类执行的操作的情况下使用,这种语法可以代替正常的Lambda语法。最后的一个示例展示了printCustom方法在这种情况下也可以使用。请注意当Lambda表达式中包含另一个表达式时的变量名轻微变化。
你可以用这种方式对任何集合进行迭代。基本的结构与增强的for循环相似,但是在类内包含迭代机制带来了许多好处。
链和过滤器
除了循环遍历一个集合的内容,你还可以将这些方法链在一起.第一个方法可以看做是一个过滤器把Predicate接口作为参数.
下面的示例是遍历一个List经过过滤之后的结果集.
Test02Filter.java
public class Test02Filter { public static void main(String[] args) { List<Person> pl = Person.createShortList(); SearchCriteria search = SearchCriteria.getInstance(); System.out.println("\n=== Western Pilot Phone List ==="); pl.stream().filter(search.getCriteria("allPilots")) .forEach(Person::printWesternName); System.out.println("\n=== Eastern Draftee Phone List ==="); pl.stream().filter(search.getCriteria("allDraftees")) .forEach(Person::printEasternName); } }
第一个和最后一个循环演示了List是如何通过查询条件来过滤的.下面是最后一个循环的输出:
=== Eastern Draftee Phone List ===Name: Baker BobAge: 21 Gender: MALEEMail: bob.baker@example.comPhone: 201-121-4678Address: 44 4th St, Smallville, KS 12333Name: Doe JohnAge: 25 Gender: MALEEMail: john.doe@example.comPhone: 202-123-4678Address: 33 3rd St, Smallville, KS 12333
越来越懒
这些特征都很有用,但为何在已经有了for循环的前提下还要把这些特性加入到集合类呢?通过把迭代功能集合到一个库中,它可以让java开发者能做到更好的代码优化.为了更好解释,我们需要了解下面的术语定义:
- Laziness(惰性):在编程中,惰性是指只处理那些你想要处理的并且你需要处理的对象.在前面的例子中,最后的循环遍历是惰性的因为它只遍历了List过滤后剩余的两个Person对象.这样代码更高效因为最终的处理步骤只发生在被选择的对象上.
- Eagerness(急切化):在List的每个对象上都执行操作的代码被认为是"急切的".例如,一个增强的for循环遍历整个List只为处理两个对象,这样可以认为是更"急切的"途径.
通过把循环集成到Collections库中,当有机会的时候代码可以更好的优化成"惰性"操作.当"急切的"做法更具有意义的时候(例如计算求和或者平均值),"急切的"操作仍可以使用.这样更高效更灵活相对与总使用"急切"操作而言.
stream方法
在前面的代码示例中,我们注意到stream方法在过滤之前和循环开始的时候被调用.这个方法将一个Collection作为输入并返回一个java.util.stream.Stream接口作为输出.一个Stream代表一个元素集合,任何方法都可以链上(个人注解:类似于建造者模式的一种写法,你可以连续调用stream的各种方法)。在默认情况下,一旦元素被消耗它们将不能从stream再次获取到(个人注解:例如调用forEach方法)。因此,一个操作链只能在一个特定的Stream上执行一次。此外,Stream可以串行(默认情况下)或者并行这依赖于方法的调用。在这个章节最后有一个并行处理的Stream示例。
变化和结果
前面提到,一个Stream在使用之后被丢弃。因此,在集合里的元素是不能使用Stream来交换或者改变。但是如果你想保存这些经过链操作之后的元素该怎么办呢?你可以将它们保存到另一个新的集合中。下面的代码将展示如何做到。
Test03toList.java
public class Test03toList { public static void main(String[] args) { List<Person> pl = Person.createShortList(); SearchCriteria search = SearchCriteria.getInstance(); // Make a new list after filtering. List<Person> pilotList = pl .stream() .filter(search.getCriteria("allPilots")) .collect(Collectors.toList()); System.out.println("\n=== Western Pilot Phone List ==="); pilotList.forEach(Person::printWesternName); } }
collect方法调用时用到一个参数Collectors类。这个Collectors类能够返回stream的结果的List或者Set。这个例子展示了stream的结果是如何分配到新的List中,并遍历这个List。
使用map计算
map方法一般和filter使用.该方法从一个类中获取某一属性并使用它.下面的例子演示如何使用年龄来计算的.
Test04Map.java
public class Test04Map { public static void main(String[] args) { List<Person> pl = Person.createShortList(); SearchCriteria search = SearchCriteria.getInstance(); // Calc average age of pilots old style System.out.println("== Calc Old Style =="); int sum = 0; int count = 0; for (Person p:pl){ if (p.getAge() >= 23 && p.getAge() <= 65 ){ sum = sum + p.getAge(); count++; } } long average = sum / count; System.out.println("Total Ages: " + sum); System.out.println("Average Age: " + average); // Get sum of ages System.out.println("\n== Calc New Style =="); long totalAge = pl .stream() .filter(search.getCriteria("allPilots")) .mapToInt(p -> p.getAge()) .sum(); // Get average of ages OptionalDouble averageAge = pl .parallelStream() .filter(search.getCriteria("allPilots")) .mapToDouble(p -> p.getAge()) .average(); System.out.println("Total Ages: " + totalAge); System.out.println("Average Age: " + averageAge.getAsDouble()); } }
这个类的输出如下:
== Calc Old Style ==Total Ages: 150Average Age: 37== Calc New Style ==Total Ages: 150Average Age: 37.5
这个程序计算在列表中所有飞行员的平均年龄.第一个循环演示了以前的方式通过for循环来计算.第二个循环使用map方法从串行流中获取每个人的年龄.注意totalAge是个long型。map方法返回一个IntStream对象,它包含sum方法返回long型。
注意:为了第二次计算平均值,不需要再计算年龄的总和。这只是为了教学演示如何使用sum方法。
最后的循环从流(Stream)中计算平均年龄。注意parallelStream方法是用来获取一个并行流这样就可以并行计算数值了。当然返回值跟上面的例子也有点不同。
资源
点击打开链接
总结
在这个教程中,你已经学习了如何使用:
- Java中的匿名内部类
- 在Java SE 8中Lambda表达式代替匿名内部类
- Lambda表达式的正确语法
- 使用Predicate接口在列表中进行搜索
- Function接口来处理对象和处理类型不一致的对象
- 在Java SE 8中Collections加入的新特征来支持Lambda表达式
- JavaSE 8 :Lambda 快速学习(四) 完结
- JavaSE 8 :Lambda 快速学习(一)
- JavaSE 8 :Lambda 快速学习(二)
- JavaSE 8 :Lambda 快速学习(三)
- Mockito 学习篇(四)完结
- Java Lambda(6)(Lambda完结)
- GCD (四) 完结
- 【JavaSE学习笔记】SE完结_反射机制,枚举Enum
- GCD介绍(四): 完结
- GCD介绍(四): 完结
- GCD介绍(四): 完结
- GCD介绍(四): 完结
- GCD教程(四):完结
- GCD介绍(四): 完结
- GCD介绍(四): 完结
- GCD介绍(四): 完结
- GCD介绍(四): 完结
- GCD介绍(四): 完结
- 【转】进程、线程、互斥量、信息量,超直观的解释(图解)
- 是男人就下100层【第一层】——高仿微信界面(9)
- Android视频客户端的设计与实现
- JavaWeb验证码
- 字典树小结
- JavaSE 8 :Lambda 快速学习(四) 完结
- scuec1111
- 转载 :我只是一直很努力
- 顺时针输出矩阵
- 拓扑排序-jobdu-1448
- 获取相机图像,返回码一直不是RESULT_OK的一个原因
- Lingoes2.9.1软件
- MFC CMenu<菜单>
- 浅析Java的垃圾回收机制