前面的章节中介绍了函数式接口并完成了一个基本的lambda表达式语法示例. 本节回顾lambda表达式如何改善集合类.html
在前面的例子中, 集合类被屡次用到. 然而, 若干新的lambda表达式特性改变了它们的使用方法. 本节讲介绍一部分这样的新特性.java
司机, 飞行员, 役男的搜索条件被封装到SearchCriteria类中web
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(); } public static SearchCriteria getInstance() { return new SearchCriteria(); } 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; } }
基于Predicate的搜索条件存储在此类中, 而且可用于咱们的测试方法.算法
第一个要注意的特性是任何集合类均可以使用的新的forEach方法. 如下是打印Person列表的几个示例.编程
package com.example.lambda; import java.util.List; /** * @author MikeW */ 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方法来打印列表中的每一个Person对象. 第二个例子演示了方法引用. 在已经存在对该类 (Person) 执行操做的方法 (printEasternName) 的状况下, 可使用该语法而不是正常的lambda表达式语法. 最后一个例子显示了在这种状况下使用printCustom方法运行. 请注意, 当一个lambda表达式包含在另外一个lambda表达式中时,变量名称稍有不一样.api
全部的集合类都能用此方法迭代. 基本结构相似于加强型for循环. 可是. 在类中包括一个迭代机制提供了许多好处.oracle
除了循环集合的内容以外,您还能够将方法连接在一块儿。第一种方法是将Predicate接口做为参数的过滤器。如下示例在首次过滤结果后循环列表。函数
package com.example.lambda; import java.util.List; /** * @author MikeW */ 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); } }
这两个循环演示了如何基于搜索条件筛选列表.测试
这些特性颇有用, 可是为何当已经有很是完美的for循环了还把它们加入到集合类中呢? 经过将迭代特性移动到库中,它容许Java的开发人员进行更多的代码优化。为了进一步说明,几个术语须要定义。优化
经过操做集合的循环, 当机会出现时,代码能够更好地针对“懒汉模式”进行优化. 而当饿汉模式更有意义时(好比, 求和或求平均), 仍然采用饿汉模式. 这种方法比老是使用渴望的操做更有效和灵活。
在前面的代码示例中,请注意,在过滤和循环开始以前调用了stream方法. 此方法使用集合类做为输入,并返回java.util.stream.Stream接口做为输出。一个steam表明了一个其上能够连接各类方法的元素序列. 默认状况下,一旦元素被消费,它们就再也不可从stream中得到。 所以, 一个操做链只能在特定的stream上发生一次. 此外, stream能够是串行(默认)或并行的,具体取决于调用的方法。本文的末尾包含有一个并行stream示例.
正如前述, stream在使用后被丢弃. 所以, 集合中的元素不能经过stream被更改或变动. 然而, 若是你想保存操做链从集合中返回的元素呢? 你能够将他们保存到一个新的集合中. 下面的代码将展现如何实现:
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对象中的.
映射方法一般和过滤器 (filter) 一块儿使用. 该方法从一个类接受一个属性, 并作一些事情. 如下示例经过基于年龄执行计算来演示此操做.
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(Person::getAge).sum(); // Get average of ages OptionalDouble averageAge = pl.parallelStream().filter(search.getCriteria("allPilots")).mapToDouble(Person::getAge).average(); System.out.println("Total Ages: " + totalAge); System.out.println("Average Age: " + averageAge.getAsDouble()); } }
这个程序计算列表中全部飞行员的平均年龄. 第一个for循环展现了旧式算法. 第二个循环经过映射方法来取得串行流中每一个person对象的年龄.
注意: 第二次计算平均值时, 并不须要先求和. 不过, 仍是做为一种启发性的展现了sum方法.
最后一个循环从stream中计算出了平均年龄. 注意它使用了parallelStream方法来获得一个并行流 (parallel stream), 从而能够同时计算这些值. 它的返回值也有稍许不一样(详见optional api).
源代码: LambdaCollectionExamples.zip
经过本教程, 你学到了如何使用:
有关Java SE 8和lambda表达式的更多信息,请参见: