【Java 20】Java8的其他新特性 - Lambda表达式、函数式接口、方法引用、构造器引用、数组引用、Stream API、Optional类

25 篇文章 0 订阅
20 篇文章 0 订阅

Java8的其他新特性

image-20200905012112313

image-20200905012147804

image-20200905012208466

1 Lambda表达式 ->

1.1 介绍

image-20200905012248279

image-20200905012253706

image-20200905012258894

1.2 格式

image-20200905012304011

1.2.1 本质

作为函数式接口(只声明了一个抽象方法)的实例

1.2.2 6种语法格式

使用总结:

  • 左边

    • lambda形参列表的参数类型可以省略(类型推断);
    • 如果lambda形参列表只有一个参数,其一对()也可以省略
  • 右边

    • lambda体应该使用一对{}包裹
    • 如果lambda体只有一条执行语句(可能有return语句),省略这一对{}和return关键字(return一定要省略)

image-20200905012309618

image-20200905012316024

1.2.3 代码实例
public class LambdaTest1 {

    //语法格式一:无参,无返回值
    @Test
    public void test1(){

        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("1");
            }
        };
        r1.run();

        /***************/

        Runnable r2 = () -> {System.out.println("2");};
        r2.run();

    }

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

        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("我好");

        /***************/

        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("他好");

        /***************/

        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("他好");

        /***************/

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

        /***************/

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

    }

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

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

        /***************/

        Comparator<Integer> com3 = (o1, o2) -> o1.compareTo(o2);
        System.out.println(com3.compare(12, 21));

    }
}

1.3 类型推断

image-20200905012322295

2 函数式(Functional)接口

  • 只包含一个抽象方法的接口,称为函数式接口。
  • 我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检 查它是否是一个函数式接口。
  • 以前用匿名实现类表示的现在都可以用Lambda表达式来写

2.1 介绍

image-20200905012332667

image-20200905012339731

2.2 函数式接口举例

image-20200905012344917

2.3 自定义函数式接口

image-20200905012349327

2.4 作为参数传递Lambda表达式

image-20200905012354711

2.5 Java内置函数式接口

image-20200905012400055

image-20200905012405802

public class LambdaTest2 {

    @Test
    public void test1(){

        happyTime(500, new Consumer<Double>() {
            @Override
            public void accept(Double aDouble) {
                System.out.println("学习好累:" + aDouble);
            }
        });

        /*******************/

        happyTime(400, money ->System.out.println("学习好累:" + money));

    }

    public void happyTime(double money, Consumer<Double> con){
        con.accept(money);
    }

    
    
    @Test
    public void test2(){

        List<String> list = Arrays.asList("北京","东京","天津");

        List<String> filterStrs = filterString(list, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("京");
            }
        });
        System.out.println(filterStrs);

        /**********************/

        List<String> filterStrs1 = filterString(list, (s) -> s.contains("京"));
        System.out.println(filterStrs1);

    }

    //根据给定的规则,过滤集合中的字符串。此规则有Predicate的方法决定
    public List<String> filterString(List<String> list, Predicate<String> pre){
        ArrayList<String> filterList = new ArrayList<>();

        for (String s : list) {
            if(pre.test(s)){
                filterList.add(s);
            }
        }

        return filterList;
    }
}

3 方法引用、构造器引用、数组引用 ::

3.1 方法引用

方法引用使用的要求:【针对情况1和情况2】

要求接口中的抽象方法的形参列表返回值类型

方法引用中的方法的形参列表返回值类型 相同

image-20200905012415760

image-20200905012420587

image-20200905012426672

  1. 情况一:对象 :: 实例方法

image-20200905162207304

image-20200905162330776

  1. 情况二:类 :: 静态方法

image-20200905163612385

image-20200905163838570

  1. 情况三:类 :: 实例方法

image-20200905165040998

image-20200905165210879

image-20200905165422799

3.2 构造器引用

和方法引用类似,函数式接口中的抽象方法的形参列表返回值类型

构造器引用中的构造器的形参列表返回值类型 相同

image-20200905012432027

image-20200905170151853

image-20200905170404744

image-20200905170533220

3.3 数组引用

把数组引用立的数组看作是一个类,写法就跟构造器引用类似

image-20200905012437589

image-20200905171009915

4 强大的Stream API

4.1 介绍

image-20200905012445726

image-20200905012452779

image-20200905012457262

4.2 操作步骤

image-20200905012501987

4.2.1 创建Stream
4.2.1.1 通过集合

image-20200905012505932

public class StreamAPITest {
    
    //创建 Stream方式一:通过集合
    @Test
    public void test1(){

        //Java8 中的 Collection 接口被扩展,提供了两个获取流的方法

        List<Employee> employees = EmployeeData.getEmployees();

        //default Stream<E> stream() : 返回一个顺序流
        Stream<Employee> stream = employees.stream();

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

    }
}
4.2.1.2 通过数组

image-20200905012510873

public class StreamAPITest {

    //创建 Stream方式二:通过数组
    @Test
    public void test2(){

        //Java8 中的 Arrays 的静态方法 stream() 可以获取数组流

        //static <T> Stream<T> stream(T[] array): 返回一个流

        int[] arr = new int[]{1,2,3,4};
        IntStream stream1 = Arrays.stream(arr);

        Employee e1 = new Employee(1001,"Wu");
        Employee e2 = new Employee(1002,"Li");
        Employee[] arr1 = new Employee[]{e1,e2};
        Stream<Employee> stream2 = Arrays.stream(arr1);

    }
}
4.2.1.3 通过Stream的of()

image-20200905012517217

public class StreamAPITest {

    //创建 Stream方式三:通过Stream的of()
    @Test
    public void test3(){

        //可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。

        //public static<T> Stream<T> of(T... values) : 返回一个流
        Stream<Integer> stream = Stream.of(1, 2, 3, 4);
    }
}
4.2.1.4 通过无限流

image-20200905012522079

image-20200905012530137

public class StreamAPITest {
    
    //创建 Stream方式四:创建无限流
    @Test
    public void test4(){

        //可以使用静态方法 Stream.iterate() 和 Stream.generate(),创建无限流

        //迭代
        //public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
        Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
        stream.limit(10).forEach(System.out::println);
        //Stream.iterate(0, x -> x + 2).limit(10).forEach(System.out::println);

        //生成
        //public static<T> Stream<T> generate(Supplier<T> s)
        Stream<Double> stream1 = Stream.generate(Math::random);
        stream1.limit(10).forEach(System.out::println);

    }
}
4.2.2 中间操作
4.2.2.1 筛选与切片

image-20200905012535785

public class StreamAPITest2 {

    @Test
    public void test1(){

        List<Employee> list = EmployeeData.getEmployees();

        //filter(Predicate p)----接收 Lambda , 从流中排除某些元素
        Stream<Employee> stream = list.stream();
        stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);

        /********************************/

        //limit(long maxSize)----截断流,使其元素不超过给定数量
        list.stream().limit(3).forEach(System.out::println);

        /********************************/

        //skip(long n)----跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
        list.stream().skip(3).forEach(System.out::println);

        /********************************/

        //distinct()----筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
        list.add(new Employee(1010,"刘强东",40,8000));
        list.add(new Employee(1010,"刘强东",40,8000));
        list.add(new Employee(1010,"刘强东",40,8000));
        list.stream().distinct().forEach(System.out::println);
    }

}
4.2.2.2 映射

image-20200905012540884

public class StreamAPITest2 {

    @Test
    public void test2(){

        List<String> list = Arrays.asList("aa", "bb", "cc");

        //add:套起来
        //map(Function f)----接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
        //mapToDouble(ToDoubleFunction f)----接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
        //mapToInt(ToIntFunction f)----接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
        //mapToLong(ToLongFunction f)----接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
        list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);

        //练习1:获取员工长度大于3的员工的姓名
        List<Employee> employees = EmployeeData.getEmployees();
        Stream<String> stringStream = employees.stream().map(Employee::getName);
        stringStream.filter(name -> name.length() > 3).forEach(System.out::println);

        //练习2:
        Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest2::fromStringToStream);
        streamStream.forEach(s -> s.forEach(System.out::println));

        //addAll:打开
        //flatMap(Function f)----接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
        Stream<Character> characterStream = list.stream().flatMap(StreamAPITest2::fromStringToStream);
        characterStream.forEach(System.out::println);

    }

    public static Stream<Character> fromStringToStream(String str){
        ArrayList<Character> list = new ArrayList<>();
        for (Character c : str.toCharArray()) {
            list.add(c);
        }
        return list.stream();
    }

}
4.2.2.3 排序

image-20200905012549186

public class StreamAPITest2 {
    
    @Test
    public void test3(){

        List<Integer> integers = Arrays.asList(12, 43, 65, 34, 57, 1, 0);
        List<Employee> employees = EmployeeData.getEmployees();

        //sorted() 产生一个新流,其中按自然顺序排序
        integers.stream().sorted().forEach(System.out::println);
        //抛异常,原因:Employee没有实现Comparable接口
        //employees.stream().sorted().forEach(System.out::println);

        //sorted(Comparator com) 产生一个新流,其中按比较器顺序排序
        //employees.stream().sorted((e1, e2) ->  Integer.compare(e1.getAge(),e2.getAge())).forEach(System.out::println);
        employees.stream().sorted((e1, e2) ->  {

            int ageValue = Integer.compare(e1.getAge(),e2.getAge());
            if(ageValue != 0){
                return ageValue;
            }else{
                return Double.compare(e1.getSalary(),e2.getSalary());
            }

        }).forEach(System.out::println);

    }

}
4.2.3 终止操作
4.2.3.1 匹配与查找

image-20200905012554998

image-20200905220942006

public class StreamAPITest3 {

    @Test
    public void test1(){

        List<Employee> list = EmployeeData.getEmployees();

        //allMatch(Predicate p)----检查是否匹配所有元素
        //练习:是否所有的员工都大于18岁
        boolean allMatch = list.stream().allMatch(e -> e.getAge() > 18);

        //anyMatch(Predicate p)----检查是否至少匹配一个元素
        //练习:是否存在员工的工资大于10000
        boolean anyMatch = list.stream().anyMatch(e -> e.getSalary() > 10000);

        //noneMatch(Predicate p)----检查是否没有匹配所有元素
        boolean noneM = list.stream().noneMatch(e -> e.getName().startsWith("雷"));

        //findFirst()----返回第一个元素
        Optional<Employee> first = list.stream().findFirst();
        System.out.println(first);
        
        //findAny()----返回当前流中的任意元素
        //Optional<Employee> any = list.stream().findAny();
        Optional<Employee> any = list.parallelStream().findAny();
        System.out.println(any);

        //count()----返回流中元素总数
        list.stream().filter(e -> e.getSalary() > 10000).count();

        //max(Comparator c)----返回流中最大值
        //返回最高工资
        Stream<Double> doubleStream1 = list.stream().map(e -> e.getSalary());
        doubleStream1.max(Double :: compare);

        //min(Comparator c)----返回流中最小值
        //返回最低工作的员工
        Optional<Employee> min = list.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(min);

        //forEach(Consumer c)----内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
        list.stream().forEach(System.out::println);//内部迭代
        list.forEach(System.out::println);//外部迭代
    }

}
4.2.3.2 归约

image-20200905012605450

public class StreamAPITest3 {

    @Test
    public void test2(){

        List<Integer> list = Arrays.asList(12, 43, 65, 34, 57, 1, 0);
        List<Employee> employees = EmployeeData.getEmployees();


        //reduce(T iden, BinaryOperator b)----可以将流中元素反复结合起来,得到一个值。返回 T
        //计算自然数的和
        Integer sum = list.stream().reduce(0, Integer::sum);
        System.out.println(sum);

        //reduce(BinaryOperator b)----可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
        //计算公司所有员工工资的和
        Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
        //Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
        Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2);
        System.out.println(sumMoney);

    }

}
4.2.3.3 收集

image-20200905012612571

image-20200905012618672

image-20200905012630355

public class StreamAPITest3 {

    @Test
    public void test3(){

        List<Employee> employees = EmployeeData.getEmployees();

        //collect(Collector c)----将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
        //查找工资大于6000的员工,结果返回为一个List或Set
        Stream<Employee> employeeStream = employees.stream().filter(e -> e.getSalary() > 6000);
        
        List<Employee> employeeList = employeeStream.collect(Collectors.toList());
        employeeList.forEach(System.out::println);

        Set<Employee> employeeSet = employeeStream.collect(Collectors.toSet());
        employeeSet.forEach(System.out::println);
    }

}

5 Optional类

5.1 介绍

image-20200905012637134

5.2 方法

image-20200905012641938

5.3 代码实例

image-20200905012647574

image-20200905012657071

public class OptionalTest {

    //一开始
    @Test
    public void test1(){

        Girl girl = new Girl();
        //girl = null;
        Optional<Girl> optionalGirl = Optional.of(girl);

    }

    //使用Optional
    @Test
    public void test2(){

        Girl girl = new Girl();
        girl = null;
        //Optional.ofNullable(T t):t可以为nul
        Optional<Girl> optionalGirl1 = Optional.ofNullable(girl);
        System.out.println(optionalGirl1);//Optional.empty

        //T orElse(T other)
        //如果当前Optional内部封装的东西,里面有值则返回,没有则返回备胎other
        Optional<Girl> optionalGirl2 = Optional.ofNullable(girl);
        Girl girl1 = optionalGirl2.orElse(new Girl("Wu小姐"));
        System.out.println(girl1);//Girl{name='Wu小姐'}

    }

    //NullPointerException情况
    @Test
    public void test3(){

        Boy boy = new Boy();
        //boy = null;//NullPointerException
        String girlName = getGirlName(boy);//NullPointerException
        System.out.println(girlName);

    }

    //获得Boy的Girl的Name
    public String getGirlName(Boy boy){
        return boy.getGirl().getName();
    }

    //没有使用Optional的空指针优化getGirlName
    public String getGirlName1(Boy boy){
        if (boy != null) {
            Girl girl = boy.getGirl();
            if (girl != null) {
                return girl.getName();
            }
        }
        return null;
    }

    //正常情况下只有一层,这里两层
    //有使用Optional的空指针优化getGirlName
    public String getGirlName2(Boy boy){
        //T orElse(T other)
        //如果当前optionalBoy里面有值则返回,没有则返回备胎other
        Optional<Boy> optionalBoy = Optional.ofNullable(boy);
        //此时的orElseBoy一定非空
        Boy orElseBoy = optionalBoy.orElse(new Boy(new Girl("1-Li小姐")));
        Girl girl = orElseBoy.getGirl();

        Optional<Girl> optionalGirl = Optional.ofNullable(girl);
        //此时的orElseGirl一定非空
        Girl orElseGirl = optionalGirl.orElse(new Girl("2-Wang小姐"));
        String name = orElseGirl.getName();

        return name;
    }

    //测试
    @Test
    public void test4(){
        //Boy-null
        Boy boy1 = null;
        String girlName1 = getGirlName2(boy1);
        System.out.println(girlName1);//1-Li小姐

        //Girl-null
        Boy boy2 = new Boy();
        String girlName2 = getGirlName2(boy2);
        System.out.println(girlName2);//2-Wang小姐

        //正常
        Boy boy3 = new Boy(new Girl("教课的苍老师"));
        String girlName3 = getGirlName2(boy3);
        System.out.println(girlName3);//教课的苍老师
    }

}

public class Boy {

    private Girl girl;

	//..........
}

public class Girl {

    private String name;
    
    //..........
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值