zoukankan      html  css  js  c++  java
  • JDK1.8新特性Lambda表达式入门

    摘要:此篇文章主要介绍 Java8 Lambda 表达式产生的背景和用法,以及 Lambda 表达式与匿名类的不同等。本文系 OneAPM 工程师编译整理。

    Java 是一流的面向对象语言,除了部分简单数据类型,Java 中的一切都是对象,即使数组也是一种对象,每个类创建的实例也是对象。在 Java 中定义的函数或方法不可能完全独立,也不能将方法作为参数或返回一个方法给实例。

    从 Swing 开始,我们总是通过匿名类给方法传递函数功能,以下是旧版的事件监听代码:

    someObject.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
    
                //Event listener implementation goes here...
    
            }
        });
    

    在上面的例子里,为了给 Mouse 监听器添加自定义代码,我们定义了一个匿名内部类 MouseAdapter 并创建了它的对象,通过这种方式,我们将一些函数功能传给 addMouseListener 方法。

    简而言之,在 Java 里将普通的方法或函数像参数一样传值并不简单,为此,Java 8 增加了一个语言级的新特性,名为 Lambda 表达式

    为什么 Java 需要 Lambda 表达式?

    如果忽视注解(Annotations)、泛型(Generics)等特性,自 Java 语言诞生时起,它的变化并不大。Java 一直都致力维护其对象至上的特征,在使用过 JavaScript 之类的函数式语言之后,Java 如何强调其面向对象的本质,以及源码层的数据类型如何严格变得更加清晰可感。其实,函数对 Java 而言并不重要,在 Java 的世界里,函数无法独立存在。

    深入浅出 Java 8 Lambda 表达式 技术分享 第1张

    在函数式编程语言中,函数是一等公民,它们可以独立存在,你可以将其赋值给一个变量,或将他们当做参数传给其他函数。JavaScript 是最典型的函数式编程语言。点击此处以及此处可以清楚了解 JavaScript 这种函数式语言的好处。函数式语言提供了一种强大的功能——闭包,相比于传统的编程方法有很多优势,闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。Java 现在提供的最接近闭包的概念便是 Lambda 表达式,虽然闭包与 Lambda 表达式之间存在显著差别,但至少 Lambda 表达式是闭包很好的替代者。

    在 Steve Yegge 辛辣又幽默的博客文章里,描绘了 Java 世界是如何严格地以名词为中心的,如果你还没看过,赶紧去读吧,写得非常风趣幽默,而且恰如其分地解释了为什么 Java 要引进 Lambda 表达式。

    Lambda 表达式为 Java 添加了缺失的函数式编程特点,使我们能将函数当做一等公民看待。尽管不完全正确,我们很快就会见识到 Lambda 与闭包的不同之处,但是又无限地接近闭包。在支持一类函数的语言中,Lambda 表达式的类型将是函数。但是,在 Java 中,Lambda 表达式是对象,他们必须依附于一类特别的对象类型——函数式接口(functional interface)。我们会在后文详细介绍函数式接口。

    Mario Fusco 的这篇思路清晰的文章介绍了为什么 Java 需要 Lambda 表达式。他解释了为什么现代编程语言必须包含闭包这类特性。

    Lambda 表达式简介

    Lambda 表达式是一种匿名函数(对 Java 而言这并不完全正确,但现在姑且这么认为),简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和名字。

    你可以将其想做一种速记,在你需要使用某个方法的地方写上它。当某个方法只使用一次,而且定义很简短,使用这种速记替代之尤其有效,这样,你就不必在类中费力写声明与方法了。

    深入浅出 Java 8 Lambda 表达式 技术分享 第2张

    Java 中的 Lambda 表达式通常使用 (argument) -> (body) 语法书写,例如:

    (arg1, arg2...) -> { body }
    
    (type1 arg1, type2 arg2...) -> { body }
    

    以下是一些 Lambda 表达式的例子:

    (int a, int b) -> {  return a + b; }
    
    () -> System.out.println("Hello World");
    
    (String s) -> { System.out.println(s); }
    
    () -> 42
    
    () -> { return 3.1415 };
    

    Lambda 表达式的结构

    让我们了解一下 Lambda 表达式的结构。

    • 一个 Lambda 表达式可以有零个或多个参数
    • 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)(a)效果相同
    • 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
    • 空圆括号代表参数集为空。例如:() -> 42
    • 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
    • Lambda 表达式的主体可包含零条或多条语句
    • 如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致
    • 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空

    什么是函数式接口

    在 Java 中,Marker(标记)类型的接口是一种没有方法或属性声明的接口,简单地说,marker 接口是空接口。相似地,函数式接口是只包含一个抽象方法声明的接口。

    java.lang.Runnable 就是一种函数式接口,在 Runnable 接口中只声明了一个方法 void run(),相似地,ActionListener 接口也是一种函数式接口,我们使用匿名内部类来实例化函数式接口的对象,有了 Lambda 表达式,这一方式可以得到简化。

    每个 Lambda 表达式都能隐式地赋值给函数式接口,例如,我们可以通过 Lambda 表达式创建 Runnable 接口的引用。

    Runnable r = () -> System.out.println("hello world");
    

    当不指明函数式接口时,编译器会自动解释这种转化:

    new Thread(
       () -> System.out.println("hello world")
    ).start();
    

    因此,在上面的代码中,编译器会自动推断:根据线程类的构造函数签名 public Thread(Runnable r) { },将该 Lambda 表达式赋给 Runnable 接口。

    以下是一些 Lambda 表达式及其函数式接口:

    Consumer<Integer>  c = (int x) -> { System.out.println(x) };
    
    BiConsumer<Integer, String> b = (Integer x, String y) -> System.out.println(x + " : " + y);
    
    Predicate<String> p = (String s) -> { s == null };
    

    @FunctionalInterface 是 Java 8 新加入的一种接口,用于指明该接口类型声明是根据 Java 语言规范定义的函数式接口。Java 8 还声明了一些 Lambda 表达式可以使用的函数式接口,当你注释的接口不是有效的函数式接口时,可以使用 @FunctionalInterface 解决编译层面的错误。

    以下是一种自定义的函数式接口: @FunctionalInterface public interface WorkerInterface {

       public void doSomeWork();
    
    }
    

    根据定义,函数式接口只能有一个抽象方法,如果你尝试添加第二个抽象方法,将抛出编译时错误。例如:

    @FunctionalInterface
    public interface WorkerInterface {
    
        public void doSomeWork();
    
        public void doSomeMoreWork();
    
    }
    

    错误:

    Unexpected @FunctionalInterface annotation 
        @FunctionalInterface ^ WorkerInterface is not a functional interface multiple 
        non-overriding abstract methods found in interface WorkerInterface 1 error
    

    函数式接口定义好后,我们可以在 API 中使用它,同时利用 Lambda 表达式。例如:

     //定义一个函数式接口
    @FunctionalInterface
    public interface WorkerInterface {
    
       public void doSomeWork();
    
    }
    
    
    public class WorkerInterfaceTest {
    
    public static void execute(WorkerInterface worker) {
        worker.doSomeWork();
    }
    
    public static void main(String [] args) {
    
        //invoke doSomeWork using Annonymous class
        execute(new WorkerInterface() {
            @Override
            public void doSomeWork() {
                System.out.println("Worker invoked using Anonymous class");
            }
        });
    
        //invoke doSomeWork using Lambda expression 
        execute( () -> System.out.println("Worker invoked using Lambda expression") );
    }
    
    }

    输出:

    Worker invoked using Anonymous class 
    Worker invoked using Lambda expression
    

    这上面的例子里,我们创建了自定义的函数式接口并与 Lambda 表达式一起使用。execute() 方法现在可以将 Lambda 表达式作为参数。

    Lambda 表达式举例

    学习 Lambda 表达式的最好方式是学习例子。

    线程可以通过以下方法初始化:

    //旧方法:
    new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello from thread");
    }
    }).start();
    
    //新方法:
    new Thread(
    () -> System.out.println("Hello from thread")
    ).start();
    

    事件处理可以使用 Java 8 的 Lambda 表达式解决。下面的代码中,我们将使用新旧两种方式向一个 UI 组件添加 ActionListener:

      //Old way:
    button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("The button was clicked using old fashion code!");
    }
    });
    
    //New way:
    button.addActionListener( (e) -> {
        System.out.println("The button was clicked. From Lambda expressions !");
    });
    

    以下代码的作用是打印出给定数组中的所有元素。注意,使用 Lambda 表达式的方法不止一种。在下面的例子中,我们先是用常用的箭头语法创建 Lambda 表达式,之后,使用 Java 8 全新的双冒号(::)操作符将一个常规方法转化为 Lambda 表达式:

    //Old way:
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    for(Integer n: list) {
       System.out.println(n);
    }
    
    //New way:
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    list.forEach(n -> System.out.println(n));
    
    
    //or we can use :: double colon operator in Java 8
    list.forEach(System.out::println);
    

    在下面的例子中,我们使用断言(Predicate)函数式接口创建一个测试,并打印所有通过测试的元素,这样,你就可以使用 Lambda 表达式规定一些逻辑,并以此为基础有所作为:

    import java.util.Arrays;
    import java.util.List;
    import java.util.function.Predicate;
    
    public class Main {
    
    public static void main(String [] a)  {
    
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    
        System.out.println("Print all numbers:");
        evaluate(list, (n)->true);
    
        System.out.println("Print no numbers:");
        evaluate(list, (n)->false);
    
        System.out.println("Print even numbers:");
        evaluate(list, (n)-> n%2 == 0 );
    
        System.out.println("Print odd numbers:");
        evaluate(list, (n)-> n%2 == 1 );
    
        System.out.println("Print numbers greater than 5:");
        evaluate(list, (n)-> n > 5 );
    
    }
    
    public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
        for(Integer n: list)  {
            if(predicate.test(n)) {
                System.out.println(n + " ");
            }
        }
    }
    
    }   
    

    输出:

    Print all numbers: 1 2 3 4 5 6 7 
    Print no numbers: 
    Print even numbers: 2 4 6 
    Print odd numbers: 1 3 5 7 
    Print numbers greater than 5: 6 7
    

    下面的例子使用 Lambda 表达式打印数值中每个元素的平方,注意我们使用了 .stream() 方法将常规数组转化为流。Java 8 增加了一些超棒的流 APIs。java.util.stream.Stream 接口包含许多有用的方法,能结合 Lambda 表达式产生神奇的效果。我们将 Lambda 表达式 x -> x*x 传给 map() 方法,该方法会作用于流中的所有元素。之后,我们使用 forEach 方法打印数据中的所有元素:

    //Old way:
    List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
    for(Integer n : list) {
        int x = n * n;
        System.out.println(x);
    }
    
    //New way:
    List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
    list.stream().map((x) -> x*x).forEach(System.out::println);
    

    下面的例子会计算给定数值中每个元素平方后的总和。请注意,Lambda 表达式只用一条语句就能达到此功能,这也是 MapReduce 的一个初级例子。我们使用 map() 给每个元素求平方,再使用 reduce() 将所有元素计入一个数值:

    //Old way:
    List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
    int sum = 0;
    for(Integer n : list) {
        int x = n * n;
        sum = sum + x;
    }
    System.out.println(sum);
    
    //New way:
    List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
    int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
    System.out.println(sum);
    

    Lambda 表达式与匿名类的区别

    使用匿名类与 Lambda 表达式的一大区别在于关键词的使用。对于匿名类,关键词 this 解读为匿名类,而对于 Lambda 表达式,关键词 this 解读为写就 Lambda 的外部类。

    Lambda 表达式与匿名类的另一不同在于两者的编译方法。Java 编译器编译 Lambda 表达式并将他们转化为类里面的私有函数,它使用 Java 7 中新加的 invokedynamic 指令动态绑定该方法,关于 Java 如何将 Lambda 表达式编译为字节码,Tal Weiss 写了一篇很好的文章

    到此为止啦,亲们!

    Mark Reinhold,甲骨文的首席架构师,将 Lambda 表达式描述为该编程模型最大的提升——比泛型(generics)还强大。事实的确如此,Lambda 表达式赋予了 Java 程序员相较于其他函数式编程语言缺失的特性,结合虚拟扩展方法之类的特性,Lambda 表达式能写出一些极好的代码。

    希望这篇文章能让您对 Java 8 的新特性所有了解。

    原文地址:http://viralpatel.net/blogs/Lambda-expressions-java-tutorial/

    下面转自:http://blog.csdn.net/renfufei/article/details/24600507

    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): 从Oracle下载Java 8,也可以和NetBeans IDE一起下载
    NetBeans IDE 8: 从NetBeans官网下载NetBeans IDE
    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>() {  
              @Override  
              public 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() {  
        @Override  
        public 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() {  
        @Override  
        public 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>() {  
        @Override  
        public 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>() {  
        @Override  
        public 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>() {  
        @Override  
        public 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>() {  
        @Override  
        public 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和Streams
    Stream是对集合的包装,通常和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中,然后显示列表:

    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集合进行排序。

    正因为当初对未来做了太多的憧憬,所以对现在的自己尤其失望。生命中曾经有过的所有灿烂,终究都需要用寂寞来偿还。
  • 相关阅读:
    单例模式
    Curator Zookeeper分布式锁
    LruCache算法原理及实现
    lombok 简化java代码注解
    Oracle客户端工具出现“Cannot access NLS data files or invalid environment specified”错误的解决办法
    解决mysql Table ‘xxx’ is marked as crashed and should be repaired的问题。
    Redis 3.0 Cluster集群配置
    分布式锁的三种实现方式
    maven发布项目到私服-snapshot快照库和release发布库的区别和作用及maven常用命令
    How to Use Convolutional Neural Networks for Time Series Classification
  • 原文地址:https://www.cnblogs.com/candlia/p/11920076.html
Copyright © 2011-2022 走看看