JDK 8 新特性之lambda表达式和Stream API

JDK 8 新特性的概述

  1. jdk版本从jdk9开始每隔半年更新一个版本

  2. 长期支持的版本(LTS):JDK8(企业使用比例最高的)、JDK11

  3. 从哪几个角度去关注新版本的特性?

    • 语法层面:lambda表达式、switch(字符串)、钻石操作符功能的扩展、接口的静态方法和默认方法和私有方法
    • API层面:Stream API、Optional、String、集合类
    • 底层优化:JVM底层的优化,GC参数,js的执行引擎
  4. Java 8 是oracle公司于2014年3月发布,可以看成是自Java 5 以来最具革命性的版本

  5. java8新特性的概述

    • 速度更快
    • 代码更少(增加了新的语法:Lambda 表达式)
    • 强大的 Stream API
    • 最大化减少空指针异常:Optional
    • Nashorn引擎,允许在JVM上运行JS应用

一、lambda表达式

1.1 规则
  1. 举例:(o1,o2) -> Integer.compare(o1,o2);

  2. 格式:
    -> 箭头操作符或lambda操作符
    ->的左边:lambda形参列表,即为原来的抽象方法的形参列表
    ->的右边:lambda体,即为接口中重写的方法的方法体

  3. lambda表达式的本质:作为函数式接口的匿名实现类的对象

  4. 函数式接口:只声明了一个抽象方法的接口。可以使用@FunctionalInterface修饰

  5. 使用规则的总结:
    ->的左边 : lambda形参列表的形参的数据类型可以省略;如果只有一个形参,则一对()可以省略
    ->的右边: lambda体使用一对{}包裹,如果执行语句只有一行,则此{}可以省略。如果有return,则return也要省略。

  6. 以前提供函数式接口的实例时,常常提供匿名实现类的对象。现在可以使用lambda表达式替换。

public class LambdaTest1 {
    //语法格式一:无参,无返回值
    @Test
    public void test1(){
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("我爱北京天安门");
            }
        };

        r1.run();

        System.out.println("***********************");
        Runnable r2 = () -> {
            System.out.println("我爱北京天安门");
        };

    }
    //语法格式二:Lambda 需要一个参数,但是没有返回值。
    @Test
    public void test2(){

        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("谎言和誓言的区别是什么?");

        System.out.println("*******************");
        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("一个是说的人当真的,一个是听的人当真了");
    }

    //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
    @Test
    public void test3(){

        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("一个是说的人当真的,一个是听的人当真了");

        System.out.println("*******************");
        Consumer<String> con2 = (s) -> {
            System.out.println(s);
        };
        con2.accept("一个是说的人当真的,一个是听的人当真了");
    }


    //语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
    @Test
    public void test4(){
        Consumer<String> con2 = (s) -> {
            System.out.println(s);
        };
        con2.accept("一个是说的人当真的,一个是听的人当真了");

        System.out.println("*******************");
        Consumer<String> con3 = s -> {
            System.out.println(s);
        };
        con3.accept("一个是说的人当真的,一个是听的人当真了");
    }

    //语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
    @Test
    public void test5(){

        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println(o1);
                System.out.println(o2);
                return o1.compareTo(o2);
            }
        };

        System.out.println(com1.compare(12,21));

        System.out.println("*****************************");

        Comparator<Integer> com2 = (o1, o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };
    }

    //语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
    @Test
    public void test6(){

        Comparator<Integer> com1 = (o1,o2) -> {
            return o1.compareTo(o2);
        };

        System.out.println(com1.compare(12,6));

        System.out.println("*****************************");

        Comparator<Integer> com2 = (o1,o2) ->  o1.compareTo(o2);

    }

    @Test
    public void test7(){
        Consumer<String> con1 = s -> {
            System.out.println(s);
        };
        con1.accept("岂曰无衣,与子同裳");
        System.out.println("*****************************");

        Consumer<String> con2 = s -> System.out.println(s);
        con2.accept("岂曰无衣,与子同裳");

    }
    @Test
    public void test8(){
        MyInterface m = ()-> System.out.println("hello");
        m.method1();
    }
}

二、Stream API

2.1 介绍
  • Stream API 之与集合,就如同SQL之与数据库。

  • Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,而Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过 CPU 实现计算。

  • 注意:

    ①Stream 自己不会存储元素。

    ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。

    ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

  • Stream操作的三个步骤

  • 1.创建 Stream
    一个数据源(如:集合、数组),获取一个流

  • 2.中间操作
    一个中间操作链,对数据源的数据进行处理

  • 3.终止操作(终端操作)
    一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用在这里插入图片描述

 * 1. Stream关注的是对数据的运算,与CPU打交道
 *    集合关注的是数据的存储,与内存打交道
 *
 * 2.
 * ①Stream 自己不会存储元素。
 * ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
 * ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
 *
 * 3.Stream 执行流程
 * ① Stream的实例化
 * ② 一系列的中间操作(过滤、映射、...)
 * ③ 终止操作
 *
 * 4.说明:
 * 4.1 一个中间操作链,对数据源的数据进行处理
 * 4.2 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用

测试Stream的实例化

public class StreamAPITest {

    //创建 Stream通过集合
    //    @Testam方式一:
    public void test1(){
        List<Employee> employees = EmployeeData.getEmployees();
//        default Stream<E> stream() : 返回一个顺序流
        Stream<Employee> stream = employees.stream();

//        default Stream<E> parallelStream() : 返回一个并行流
        Stream<Employee> stream1 = employees.parallelStream();
    }

    //创建 Stream方式二:通过数组
    @Test
    public void test2(){
        //调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流
        Integer[] arr = new Integer[]{1,2,3,4,5};
        Stream<Integer> stream = Arrays.stream(arr);

        Employee e1 = new Employee();
        Employee e2 = new Employee();
        Employee[] emps = new Employee[]{e1,e2};
        Stream<Employee> stream1 = Arrays.stream(emps);
    }
    //创建 Stream方式三:通过Stream的of()
    @Test
    public void test3(){
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);

        Employee e1 = new Employee();
        Employee e2 = new Employee();
        Stream<Employee> stream1 = Stream.of(e1, e2);
    }
}

Stream的中间操作

public class StreamAPITest1 {

    //1-筛选与切片
    @Test
    public void test1() {
        List<Employee> emps = EmployeeData.getEmployees();

//        filter(Predicate p)——接收 Lambda , 从流中排除某些元素。
        //练习:查询员工表中薪资大于7000的员工信息
        Stream<Employee> stream = emps.stream();
        stream.filter(e -> e.getSalary() > 7000).forEach(e -> System.out.println(e));
//        limit(n)——截断流,使其元素不超过给定数量。
        System.out.println();
        Stream<Employee> stream1 = emps.stream();
        stream1.limit(2).forEach(e -> System.out.println(e));
//        skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
        System.out.println();
        Stream<Employee> stream2 = emps.stream();
        stream2.skip(3).forEach(e -> System.out.println(e));
        System.out.println();
//        distinct()——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素

        emps.add(new Employee(1009, "任正非", 65, 12500.32));
        emps.add(new Employee(1009, "任正非", 65, 12500.32));
        emps.add(new Employee(1009, "任正非", 65, 12500.32));
        emps.add(new Employee(1009, "任正非", 65, 12500.32));
        emps.add(new Employee(1009, "任正非", 65, 12500.32));

        emps.stream().distinct().forEach(e -> System.out.println(e));
    }

    //2-映射
    @Test
    public void test2() {
        //map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
        List<String> list = Arrays.asList("ab", "cc", "jj", "ui");
        list.stream().map(str -> str.toUpperCase()).forEach(s -> System.out.println(s));
        //练习:获取员工姓名长度大于3的员工的姓名。
        List<Employee> emps = EmployeeData.getEmployees();
        Stream<Employee> stream = emps.stream();
//        stream.filter(e -> e.getName().length() > 3).forEach(s -> System.out.println(s));

        Stream<String> stringStream = stream.map(e -> e.getName());
        stringStream.filter(s -> s.length() > 3).forEach(s -> System.out.println(s));
    }

    //3-排序
    @Test
    public void test3() {
        //sorted()——自然排序
        Integer[] arr = new Integer[]{5,6,7,23,11,4,65,4};
        Stream<Integer> stream = Arrays.stream(arr);
        stream.sorted().forEach(i -> System.out.println(i));

        //Employee由于没有实现Comparable接口,不能使用自然排序
//        Employee e1 = new Employee(1001, "马化腾", 34, 6000.38);
//        Employee e2 = new Employee(1002, "马云", 12, 9876.12);
//        Employee[] emps = new Employee[]{e1,e2};
//        Arrays.stream(emps).sorted().forEach(e -> System.out.println(e));

        //sorted(Comparator com)——定制排序
        List<Employee> employees = EmployeeData.getEmployees();
        employees.stream().sorted((emp1,emp2) -> {
            int result = Integer.compare(emp1.getAge(),emp2.getAge());
            if(result != 0){
                return result;
            }
            return Double.compare(emp1.getSalary(),emp2.getSalary());
        }).forEach(i -> System.out.println(i));
    }
}

Stream的终止操作

public class StreamAPITest2 {

    //1-匹配与查找
    @Test
    public void test1(){
        List<Employee> employees = EmployeeData.getEmployees();

//        allMatch(Predicate p)——检查是否匹配所有元素。
//          练习:是否所有的员工的年龄都大于18
        boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
        System.out.println(allMatch);
//        anyMatch(Predicate p)——检查是否至少匹配一个元素。
//         练习:是否存在员工的工资大于 10000
        boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
        System.out.println(anyMatch);
//        findFirst——返回第一个元素
        Optional<Employee> optional = employees.stream().findFirst();
        System.out.println(optional.get());
    }

    @Test
    public void test2(){
        List<Employee> employees = EmployeeData.getEmployees();
        // count——返回流中元素的总个数
        System.out.println(employees.stream().filter(e -> e.getAge() > 18).count());
//        max(Comparator c)——返回流中最大值
//        练习:返回最高的工资:
        Stream<Double> doubleStream = employees.stream().map(e -> e.getSalary());
        Optional<Double> optional = doubleStream.max((s1, s2) -> Double.compare(s1, s2));
        System.out.println(optional.get());
//        min(Comparator c)——返回流中最小值
//        练习:返回最低工资的员工
        Employee employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).get();
        System.out.println(employee);
//        forEach(Consumer c)——内部迭代
        //略
        employees.stream().forEach(System.out::println);
        //集合中新增的方法:forEach
        employees.forEach( e-> System.out.println(e));
    }

    //2-归约
    @Test
    public void test3(){
//        reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T
//        练习1:计算1-10的自然数的和
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = list.stream().reduce(0, (x1, x2) -> x1 + x2);
        System.out.println(sum);

//        reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
//        练习2:计算公司所有员工工资的总和
        Stream<Employee> stream = EmployeeData.getEmployees().stream();
        Stream<Double> stream1 = stream.map(e -> e.getSalary());
        Optional<Double> optional = stream1.reduce((s1, s2) -> s1 + s2);
        System.out.println(optional.get());
    }

    //3-收集
    @Test
    public void test4(){
//        collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
//        练习1:查找工资大于6000的员工,结果返回为一个List或Set

        Stream<Employee> stream = EmployeeData.getEmployees().stream();
        List<Employee> list = stream.filter(e -> e.getSalary() > 6000).collect(Collectors.toList());

        list.forEach(System.out::println);
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值