JavaSE 8 :Lambda 快速学习(四) 完结

来源:互联网 发布:淘宝网沙发 编辑:程序博客网 时间:2024/05/05 06:14

Lambda Expressions and Collections

前面的章节已经介绍过Function接口,也已经实现基本的Lambda表达式语法示例.这一章节我们重新回顾Lambda表达式如何通过Collection类来提升.

Lambda Expressions and Collections

到目前所创建的示例中,集合只用到一点.但是一些Lambda表达式新特征改变了集合以往的使用方式.这一章节将介绍这些新特征.

新增类

Drivers,pilotsdraftees查询条件都已经被封装到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表达式

0 0
原创粉丝点击