java8 lambda表达式与集合类批处理操作

jopen 10年前

λ(lambda)表达式本质上是一个匿名方法,本文主要讲解 lambda表达式与集合类批处理操作

一、基本概念

    λ表达式可以被当做是一个Object。λ表达式的类型,叫做“目标类型(target type)”。λ表达式的目标类型是“函数接口(functional interface)”,这是Java8新引入的概念。它的定义是:一个接口,如果只有一个显式声明的抽象方法,那么它就是一个函数接口。一般用@FunctionalInterface标注出来(也可以不标)

@FunctionalInterface  public interface Runnable { void run(); }        public interface Callable<V> { V call() throws Exception; }        public interface ActionListener { void actionPerformed(ActionEvent e); }        public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }
    注意最后这个Comparator接口。它里面声明了两个方法,貌似不符合函数接口的定义,但它的确是函数接口。这是因为equals方法是Object的,所有的接口都会声明Object的public方法——虽然大多是隐式的。所以,Comparator显式的声明了equals不影响它依然是个函数接口

    集合类的批处理操作API的目的是实现集合类的“内部迭代”,并期望充分利用现代多核CPU进行并行计算。
    Java8之前集合类的迭代(Iteration)都是外部的,即客户代码,不能充分利用cpu的多核资源。而内部迭代意味着改由Java类库来进行迭代,而不是客户代码.

    Java8为集合类引入了一个重要概念:流(stream)。一个流通常以一个集合类实例为其数据源,然后在其上定义各种操作。流的API设计使用了管道(pipelines)模式。对流的一次操作会返回另一个流。如同IO的API或者StringBuffer的append方法那样,从而多个不同的操作可以在一个语句里串起来.

二、λ表达式的使用

    一个λ表达式只有在转型成一个函数接口后才能被当做Object使用

    可以用一个λ表达式为一个函数接口赋值,然后再赋值给一个Object

    一个λ表达式可以有多个目标类型(函数接口),只要函数匹配成功即可。但需注意一个λ表达式必须至少有一个目标类型

    λ表达式主要用于替换以前广泛使用的内部匿名类,各种回调,比如事件响应器、传入Thread类的Runnable等

new Thread( () -> {          System.out.println("This is from an anonymous method (lambda exp).");      } );
    注意线程里的λ表达式,你并不需要显式地把它转成一个Runnable,因为Java能根据上下文自动推断出来:一个Thread的构造函数接受一个Runnable参数,而传入的λ表达式正好符合其run()函数,所以Java编译器推断它为Runnable。

    filter方法的参数是Predicate类型,forEach方法的参数是Consumer类型,它们都是函数接口,所以可以使用λ表达式

三、代码样例

import java.util.ArrayList;  import java.util.Arrays;  import java.util.List;  import java.util.Map;  import java.util.concurrent.Callable;  import java.util.stream.Collectors;  import java.util.stream.IntStream;    public class Main {   private static List<Person> persons = Arrays.asList(new Person("Joe", 12),     new Person("Jim", 19), new Person("John", 21));   public static void main(String[] args) throws Exception {    // testStreamAPI();    // testStreamMap();    // testStreamPerformance();    testInt(2, 3, 4, 2, 3, 5, 1);    testOccurrence(2, 3, 4, 2, 3, 5, 1);    distinctSum(2, 3, 4, 2, 3, 5, 1);    testNestLambda();   }     public static void testStreamAPI() {    // 打印年龄大于12的人    System.out.println("使用顺序流串行打印");    persons.stream().filter(p -> p.getAge() > 12)      .collect(Collectors.toCollection(ArrayList::new))      .forEach(p -> {       System.out.println(p);      });    System.out.println("使用并行流并行打印,即利用多核技术可将大数据通过多核并行处理");    persons.parallelStream().filter(p -> p.getAge() > 12)      .collect(Collectors.toCollection(ArrayList::new))      .forEach(p -> {       System.out.println(p);      });   }     public static void testStreamMap() {    // 应该用filter过滤,然后再使用map进行转换    persons.parallelStream().map(p -> {     if (p.getAge() > 18)      return p;     return null;    }).collect(Collectors.toCollection(ArrayList::new)).forEach(p -> {     if (p != null)      System.out.println(p);    });      persons.parallelStream().filter(p -> p.getAge() > 18)      .map(p -> new Adult(p))      .collect(Collectors.toCollection(ArrayList::new))      .forEach(p -> {       if (p != null)        System.out.println(p);      });   }     public static void testStreamReduce() {    persons.parallelStream().filter(p -> p.getAge() > 18)      .map(p -> new Adult(p))      .collect(Collectors.toCollection(ArrayList::new))      .forEach(p -> {       if (p != null)        System.out.println(p);      });   }     public static void testStreamPerformance() {    // 初始化一个范围100万整数流,求能被2整除的数字,toArray()是终点方法    long start1 = System.nanoTime();    int a[] = IntStream.range(0, 1_000_000).filter(p -> p % 2 == 0)      .toArray();    System.out.printf("测试顺序流的性能: %.2fs",      (System.nanoTime() - start1) * 1e-9);      long start2 = System.nanoTime();    int b[] = IntStream.range(0, 1_000_000).parallel()      .filter(p -> p % 2 == 0).toArray();    System.out.printf(" 测试并行流的性能: %.2fs",      (System.nanoTime() - start2) * 1e-9);    // 本机的测试结果是:测试顺序流的性能: 0.02s 测试并行流的性能: 0.01s    // 在100万时,并发流快些,1000万,并发流反而会慢些,估计和线程的频繁切换有关(本机是8线程CPU)   }     public static void testInt(Integer... numbers) {    List<Integer> l = Arrays.asList(numbers);    List<Integer> r = l.stream()      .map(e -> new Integer(e))      .filter(e -> e > 2)      .distinct()      .collect(Collectors.toList());    System.out.println("testInt result is: " + r);   }     public static void testOccurrence(Integer... numbers) {    List<Integer> l = Arrays.asList(numbers);    Map<Integer, Integer> r = l      .stream()      .map(e -> new Integer(e))      .collect(        Collectors.groupingBy(p -> p,          Collectors.summingInt(p -> 1)));    System.out.println("testOccurrence result is: " + r);   }     public static void distinctSum(Integer... numbers) {    List<Integer> l = Arrays.asList(numbers);    int sum = l.stream()      .map(e -> new Integer(e))      .distinct()      .reduce(0, (x, y) -> x + y); // equivalent to .sum()    System.out.println("distinctSum result is: " + sum);   }      public static void testNestLambda() throws Exception{     Callable<Runnable> c1 = () -> () -> { System.out.println("Nested lambda"); };     c1.call().run();    // 用在条件表达式中    Callable<Integer> c2 = false ? (() -> 42) : (() -> 24);    System.out.println(c2.call());   }  }    class Person {   private String name;   private int age;     public Person(String name, int age) {    this.name = name;    this.age = age;   }     public void setName(String name) {    this.name = name;   }     public String getName() {    System.out.println(name);    return name;   }     public int getAge() {    return age;   }     @Override   public String toString() {    return "name:" + name + " age:" + age;   }  }    class Adult extends Person {   public Adult(Person p) {    super(p.getName(), p.getAge());   }  }