• Java8新特性--Lambda表达式


    从java8出现以来lambda是最重要的特性之一,它可以让我们用简洁流畅的代码完成一个功能。 很长一段时间java被吐槽是冗余和缺乏函数式编程能力的语言,随着函数式编程的流行java8种也引入了 这种编程风格。

    什么是lambda?

    lambda表达式是一段可以传递的代码,它的核心思想是将面向对象中的传递数据变成传递行为。 我们回顾一下在使用java8之前要做的事,之前我们编写一个线程时是这样的:

    1
    2
    3
    4
    5
    6
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("do something.");     
        }
    }

    也有人会写一个类去实现Runnable接口,这样做没有问题,我们注意这个接口中只有一个run方法, 当把Runnable对象给Thread对象作为构造参数时创建一个线程,运行后将输出do something.。 我们使用匿名内部类的方式实现了该方法。

    这实际上是一个代码即数据的例子,在run方法中是线程要执行的一个任务,但上面的代码中任务内容已经被规定死了。 当我们有多个不同的任务时,需要重复编写如上代码。

    设计匿名内部类的目的,就是为了方便 Java 程序员将代码作为数据传递。不过,匿名内部 类还是不够简便。 为了执行一个简单的任务逻辑,不得不加上 6 行冗繁的样板代码。那如果是lambda该怎么做?

    1
    Runnable r = () -> System.out.println("do something.");

    嗯,这代码看起来很酷,你可以看到我们用()和->的方式完成了这件事,这是一个没有名字的函数,也没有人和参数,再简单不过了。 使用->将参数和实现逻辑分离,当运行这个线程的时候执行的是->之后的代码片段,且编译器帮助我们做了类型推导; 这个代码片段可以是用{}包含的一段逻辑。下面一起来学习一下lambda的语法。

    基础语法

    在lambda中我们遵循如下的表达式来编写:

    1
    expression = (variable) -> action
    • variable: 这是一个变量,一个占位符。像x,y,z,可以是多个变量;
    • action: 这里我称它为action, 这是我们实现的代码逻辑部分,它可以是一行代码也可以是一个代码片段。

    可以看到Java中lambda表达式的格式:参数、箭头、以及动作实现,当一个动作实现无法用一行代码完成,可以编写 一段代码用{}包裹起来。

    lambda表达式可以包含多个参数,例如:

    1
    int sum = (x, y) -> x + y;

    这时候我们应该思考这段代码不是之前的x和y数字相加,而是创建了一个函数,用来计算两个操作数的和。 后面用int类型进行接收,在lambda中为我们省略去了return。

    函数式接口

    函数式接口是只有一个方法的接口,用作lambda表达式的类型。前面写的例子就是一个函数式接口,来看看jdk中的Runnable源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @FunctionalInterface
    public interface Runnable {
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see     java.lang.Thread#run()
         */
        public abstract void run();
    }

    这里只有一个抽象方法run,实际上你不写public abstract也是可以的,在接口中定义的方法都是public abstract的。 同时也使用注解@FunctionalInterface告诉编译器这是一个函数式接口,当然你不这么写也可以,标识后明确了这个函数中 只有一个抽象方法,当你尝试在接口中编写多个方法的时候编译器将不允许这么干。

    尝试函数式接口

    我们来编写一个函数式接口,输入一个年龄,判断这个人是否是成人。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class FunctionInterfaceDemo {
        @FunctionalInterface
        interface Predicate<T> {
            boolean test(T t);
        }
        /**
         * 执行Predicate判断
         *
         * @param age       年龄
         * @param predicate Predicate函数式接口
         * @return          返回布尔类型结果
         */
        public static boolean doPredicate(int age, Predicate<Integer> predicate) {
            return predicate.test(age);
        }
         
        public static void main(String[] args) {
            boolean isAdult = doPredicate(20, x -> x >= 18);
            System.out.println(isAdult);
        }
    }

    从这个例子我们很轻松的完成 是否是成人 的动作,其次判断是否是成人,在此之前我们的做法一般是编写一个 判断是否是成人的方法,是无法将 判断 共用的。而在本例只,你要做的是将 行为 (判断是否是成人,或者是判断是否大于30岁) 传递进去,函数式接口告诉你结果是什么。

    实际上诸如上述例子中的接口,伟大的jdk设计者为我们准备了java.util.function包

    我们前面写的Predicate函数式接口也是JDK种的一个实现,他们大致分为以下几类:

    消费型接口示例

    1
    2
    3
    4
    5
    6
    public static void donation(Integer money, Consumer<Integer> consumer){
        consumer.accept(money); 
    }
    public static void main(String[] args) {
        donation(1000, money -> System.out.println("好心的麦乐迪为Blade捐赠了"+money+"元")) ;
    }

    供给型接口示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public static List<Integer> supply(Integer num, Supplier<Integer> supplier){
           List<Integer> resultList = new ArrayList<Integer>()   ;
           for(int x=0;x<num;x++) 
               resultList.add(supplier.get());
           return resultList ;
    }
    public static void main(String[] args) {
        List<Integer> list = supply(10,() -> (int)(Math.random()*100));
        list.forEach(System.out::println);
    }

    函数型接口示例

    转换字符串为Integer

    1
    2
    3
    4
    5
    6
    public static Integer convert(String str, Function<String, Integer> function) {
        return function.apply(str);
    }
    public static void main(String[] args) {
        Integer value = convert("28", x -> Integer.parseInt(x));
    }

    断言型接口示例

    筛选出只有2个字的水果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public static List<String> filter(List<String> fruit, Predicate<String> predicate){
        List<String> f = new ArrayList<>();
        for (String s : fruit) {
            if(predicate.test(s)){
                f.add(s);
            }
        }
        return f;
    }
    public static void main(String[] args) {
        List<String> fruit = Arrays.asList("香蕉", "哈密瓜", "榴莲", "火龙果", "水蜜桃");
        List<String> newFruit = filter(fruit, (f) -> f.length() == 2);
        System.out.println(newFruit);
    }

    默认方法

    在Java语言中,一个接口中定义的方法必须由实现类提供实现。但是当接口中加入新的API时, 实现类按照约定也要修改实现,而Java8的API对现有接口也添加了很多方法,比如List接口中添加了sort方法。 如果按照之前的做法,那么所有的实现类都要实现sort方法,JDK的编写者们一定非常抓狂。

    幸运的是我们使用了Java8,这一问题将得到很好的解决,在Java8种引入新的机制,支持在接口中声明方法同时提供实现。 这令人激动不已,你有两种方式完成 1.在接口内声明静态方法 2.指定一个默认方法。

    我们来看看在JDK8中上述List接口添加方法的问题是如何解决的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

    翻阅List接口的源码,其中加入一个默认方法default void sort(Comparator<? super E> c)。 在返回值之前加入default关键字,有了这个方法我们可以直接调用sort方法进行排序。

    1
    2
    3
    List<Integer> list = Arrays.asList(2, 7, 3, 1, 8, 6, 4);
    list.sort(Comparator.naturalOrder());
    System.out.println(list);

    Comparator.naturalOrder()是一个自然排序的实现,这里可以自定义排序方案。你经常看到使用Java8操作集合的时候可以直接foreach的原因也是在Iterable接口中也新增了一个默认方法:forEach,该方法功能和 for 循环类似,但是允许 用户使用一个Lambda表达式作为循环体。

    例子:

    public class LambdaTest {


    public static void main(String[] arg){

    int num = Runtime.getRuntime().availableProcessors();
    System.out.println("处理器数: " + num);

    //1.用lambda表达式实现Runnable
    // Java 8之前:
    new Thread(new Runnable() {
    @Override
    public void run() {
    System.out.println("Before Java8, too much code for too little to do");
    }
    }).start();
    //Java 8方式:
    new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();
    new Thread( () -> {
    System.out.println("In Java8, Lambda expression rocks !!");
    }).start();


    //2.使用lambda表达式对列表进行迭代
    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);


    //3.使用Lambdas排序集合
    //使用匿名内部类进行排序
    // players.sort(new Comparator<String>() {
    // @Override
    // public int compare(String o1, String o2) {
    // return (o1.compareTo(o2));
    // }
    // });
    // System.out.println("排序后: ");
    // players.forEach((player) -> System.out.print(player + "; "));
    //使用Lambda
    Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));
    Collections.sort(players, sortByName);
    System.out.println("排序后: ");
    players.forEach((player) -> System.out.print(player + "; "));


    //使用lambda表达式和函数式接口Predicate
    System.out.println("测试Predicate");
    testPredicate();



    }


    public static void testPredicate(){
    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));
    }
    };


    System.out.println("所有java程序员的姓名:");
    javaProgrammers.forEach((p) ->System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

    System.out.println("所有php程序员的姓名:");
    phpProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));


    //Consumer是java8的函数式接口
    System.out.println("给程序员加薪 5% :");
    Consumer<Person> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());
    javaProgrammers.forEach(giveRaise);
    phpProgrammers.forEach(giveRaise);



    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()));


    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()));


    //排序
    System.out.println("根据 name 排序,并显示前5个 Java programmers:");
    List<Person> sortedJavaProgrammers = javaProgrammers
    .stream()
    .sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
    .limit(5)
    .collect(Collectors.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(Collectors.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());




    System.out.println("将 PHP programmers 的 first name 拼接成字符串:");
    String phpDevelopers = phpProgrammers
    .stream()
    .map(Person::getFirstName)
    .collect(Collectors.joining(" ; ")); // 在进一步的操作中可以作为标记(token)
    System.out.println(phpDevelopers);

    System.out.println("将 Java programmers 的 first name 存放到 Set:");
    Set<String> javaDevFirstName = javaProgrammers
    .stream()
    .map(Person::getFirstName)
    .collect(Collectors.toSet());

    System.out.println("将 Java programmers 的 first name 存放到 TreeSet:");
    TreeSet<String> javaDevLastName = javaProgrammers
    .stream()
    .map(Person::getLastName)
    .collect(Collectors.toCollection(TreeSet::new));


    //Streams 还可以是并行的(parallel). Stream是对集合的包装
    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());
    }
    }
    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;
    }

    public String getFirstName() {
    return firstName;
    }

    public void setFirstName(String firstName) {
    this.firstName = firstName;
    }

    public String getLastName() {
    return lastName;
    }

    public void setLastName(String lastName) {
    this.lastName = lastName;
    }

    public String getJob() {
    return job;
    }

    public void setJob(String job) {
    this.job = job;
    }

    public String getGender() {
    return gender;
    }

    public void setGender(String gender) {
    this.gender = gender;
    }

    public int getSalary() {
    return salary;
    }

    public void setSalary(int salary) {
    this.salary = salary;
    }

    public int getAge() {
    return age;
    }

    public void setAge(int age) {
    this.age = age;
    }
    }
  • 相关阅读:
    APUE_1.7ErrorHandling Figure1.8Demonstrate strerror and perror
    4-7 使用tf.train.slice_input_producer读取列表样本
    Ubuntu16.4+github+pycharm
    4-5 Cifar10数据集解析
    4-6 TF之TFRecord数据打包案例
    matlab如何创建一个视频预览
    Alpha
    如何使用 declare-styleable
    LayoutParams使用
    桌面快捷方式的问题-创建-删除-判断
  • 原文地址:https://www.cnblogs.com/kexianting/p/8588987.html
Copyright © 2020-2023  润新知