【Java SE】15. Java8的型特性

15.1 Lambda表达式

  1. Lambda的本质:一个接口的实例,且该接口只能是一个函数式接口(只有一个抽象方法:实例化时,不需要写方法名,唯一没有歧义)

  2. 从匿名类到 Lambda 的转换举例:

    //匿名类
    Comparator<Integer> comparator1 = new Comparator<>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return Integer.compare(o1,o2);
        }
    };
    int compare1 = comparator1.compare(12, 31);
    System.out.println(compare1);
    
    //Lambda表达式写法
    Comparator<Integer> comparator2 =(o1,o2) -> Integer.compare(o1,o2);
    int compare2 = comparator2.compare(51, 31);
    System.out.println(compare2);
    
    //方法引用
    Comparator<Integer> comparator3 = Integer :: compare;
    int compare3 = comparator3.compare(12, 31);
    System.out.println(compare3);
    
  3. Lambda 表达式: 在Java 8 语言中引入的一种新的语法元素和操作符

  4. Lambda 操作符 “->” ,它将 Lambda 分为两个部分:

    1. 左侧: 指定了 Lambda,表达式需要的参数列表
    2. 右侧: 指定了 Lambda 体,是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能
  5. Lambda表达式的使用(6种情况)

    1. 无参,无返回值

      Runnable r1 = new Runnable() {
          @Override
          public void run() {
              System.out.println("hh");
          }
      };
      r1.run();
      
      Runnable r2 = () -> System.out.println("hh");
      r1.run();
      
    2. Lambda 需要一个参数,但是没有返回值

      Consumer<String> consumer1 = new Consumer<String>() {
          @Override
          public void accept(String s) {
              System.out.println(s);
          }
      };
      consumer1.accept("hh");
      
      Consumer<String> consumer2 = (String s) -> System.out.println(s);
      consumer2.accept("hh");
      
    3. 数据类型可以省略,因为可由编译器推断得出,称为“类型推断”

      Consumer<String> consumer2 = (s) -> System.out.println(s);
      
    4. Lambda 若只需要一个参数时, 参数的小括号可以省略

      Consumer<String> consumer2 = s -> System.out.println(s);
      
    5. Lambda 需要两个或以上的参数多条执行语句并且有返回值

      Comparator<Integer> comparator2 =(o1, o2) ->{
          System.out.println(o1);
          System.out.println(o2);
          return o1.compareTo(o2);
      };
      
      int compare2 = comparator2.compare(51, 31);
      System.out.println(compare2);
      
    6. 当 Lambda 体只有一条语句时, return与大括号若有,都可以省略

15.2 函数式(Functional)接口

  1. 如果一个接口中只声明了一个抽象方法,则就是函数式接口

  2. 可以通过 Lambda 表达式来创建该接口的对象

  3. 可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口

  4. Java 内置四大核心函数式接口

    在这里插入图片描述

  5. 其他接口

    在这里插入图片描述

15.3 方法引用与构造器引用

  1. **使用情形:**当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!

  2. 方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法

  3. **要求:**实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!

  4. 格式: 使用操作符 “: :” 将类(或对象) 与 方法名分隔开来,左侧调用者,右侧被调方法

  5. 如下三种主要使用情况:

    1. 对象 :: 实例方法名

      Comsumer<String> con = (x) -> System.out.println(x);
      //等同于
      Comsumer<String> con = System.out :: println;
      
    2. 类::静态方法名

    3. 类::实例方法名

15.4 强大的Stream API

  1. Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中

  2. 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之, Stream API 提供了一种高效且易于使用的处理数据的方式

  3. 实际开发中,项目中多数数据源都来自于Mysql, Oracle等。但现在数据源可以更多了,有MongDB, Radis等,而这些NoSQL的数据就需要Java层面去处理

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

  5. 集合讲的是数据, Stream讲的是计算

    1. Stream 自己不会存储元素
    2. Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream
    3. Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
  6. Stream 的操作三个步骤

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

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

    3. 终止操作(终端操作):一旦执行终止操作, 就执行中间操作链,并产生结果。之后,不会再被使用

      在这里插入图片描述

  7. Stream的实例化

    1. 方式一:通过集合实例化:

      class getData {
          public static List<Person> getArrayList(){
              ArrayList<Person> peoples = new ArrayList<>();
              
              peoples.add(new Person("Tom",23));
              peoples.add(new Person("Jack",22));
              peoples.add(new Person("Mike",21));
              peoples.add(new Person("Ted",20));
              return peoples;
          }
      }
      
      public class test{
          public static void main(String[] args) {
              List<Person> peoples = getData.getArrayList();
              
              //  default Steam<E> stream() : 返回一个顺序流
              Stream<Person> stream = peoples.stream();
      
              //  default Steam<E> parallelStream() : 返回一个并行流
              Stream<Person> personStream = peoples.parallelStream();
          }
      }
      
    2. 方式二:通过数组实例化:

      //调用Array类的static <T> stream(T[] array): 返回一个流
        int[] ints = new int[]{1,2,3,4,5};
        IntStream stream1 = Arrays.stream(ints);
      
        Person p1 = new Person("Tom", 22);
        Person p2 = new Person("Jack", 22);
        Person[] peoples = {p1, p2};
        Stream<Person> stream2 = Arrays.stream(peoples);
      
    3. 方式三:通过Stream的of( )

      Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
      
    4. 方式四:创建无限流

      //迭代 :public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
      Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);
      //生成 :public static<T> Stream<T> generate(Supplier<T> s)
      Stream.generate(Math::random).limit(10).forEach(System.out::println);
      
  8. Stream的中间操作

    1. 筛选与切片:

      1. filter(Predicate p):接收 Lambda , 从流中排除某些元素

      2. distinct(n):筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素

      3. limit(long maxSize):截断流,使其元素不超过给定数量

      4. skip(long n):跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一 个空流。与 limit(n) 互补

        List<Person> peoples = getData.getArrayList();
        Stream<Person> stream = peoples.stream();
        
        //1.filter(Predicate p):过滤
        stream.filter(e -> e.getAge() > 20).forEach(System.out::println);
        //只有当终止操作forEach()执行时,才会产生结果
        
        //2.limit(n)
        //stream.limit(3).forEach(System.out::println);//IllegalStateException
        peoples.stream().limit(3).forEach(System.out::println);
        
        
        //3.skip(n)
        peoples.stream().skip(1).forEach(System.out::println);
        
        //4.distinct():去重(元素类一定要实现hashCode()和equals())
        peoples.add(new Person("Tom",23));
        peoples.add(new Person("Tom",23));
        peoples.stream().distinct().forEach(System.out::println);
        
    2. 映射:

      1. map(Function f):接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素

        List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
        //map(Function f)
        list.stream().map(e -> e.toUpperCase()).forEach(System.out::println);
        //AA BB CC DD
        
        //map练习:找出名字长度大于3的人的名字
        List<Person> peoples = getData.getArrayList();
        Stream<String> namesstream = peoples.stream().map(e -> e.getName());
        namesstream.filter(name -> name.length() > 3).forEach(System.out::println);
        //Jack Mike
        
      2. flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另 一个流,然后把所有流连接成一个流

        //类比理解:
        ArrayList list1 = new ArrayList();
        list1.add(1);
        list1.add(2);
        list1.add(3);
        
        ArrayList list2 = new ArrayList();
        list2.add(4);
        list2.add(5);
        list2.add(6);
        
        //1.
        //list1.add(list2);
        //System.out.println(list1);
        //[1, 2, 3, [4, 5, 6]],集合里面套集合
        
        //2.
        list1.addAll(list2);
        System.out.println(list1);
        //[1, 2, 3, 4, 5, 6],将list2拆开放入list1
        
        public class test{
            
            //此方法返回一个stream流
            public static Stream<Character> fromStringToStream(String str){
                ArrayList<Character> list = new ArrayList<>();
                for (Character c : str.toCharArray()
                     ) {
                    list.add(c);
                }
                return list.stream();
            }
            
            public static void main(String[] args) {
                List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
                
                //flatMap(Function f)
                
                //每次将list中的每个字符串传入方法后,就得到一个流
                //如果使用map会得到流中包含流
                Stream<Stream<Character>> stream1 = list.stream().map(s -> test.fromStringToStream(s));
                stream1.forEach(System.out::println);
                //结果:流中的流
                //java.util.stream.ReferencePipeline$Head@568db2f2
                //java.util.stream.ReferencePipeline$Head@378bf509
                //java.util.stream.ReferencePipeline$Head@5fd0d5ae
                //java.util.stream.ReferencePipeline$Head@2d98a335
        
                //如果使用flatMap会将每个流的内容拿出来,合到一个流中
                Stream<Character> stream = list.stream().flatMap(s -> test.fromStringToStream(s));
                stream.forEach(System.out::print);
                //a a b b c c d d
            }
        }
        
      3. mapToDouble(ToDoubleFunction f):接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 DoubleStream

      4. mapToInt(ToIntFunction f):接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 IntStream

      5. mapToLong(ToLongFunction f):接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 LongStream

    3. 排序:

      1. sorted():产生一个新流,其中按自然顺序排序

        List<String> list = Arrays.asList("aa", "bb", "dd", "cc");
        list.stream().sorted().forEach(System.out::println);
        
      2. sorted(Comparator com):产生一个新流,其中按比较器顺序排序

        //List<Person> peoples = getData.getArrayList();
        //peoples.stream().sorted().forEach(System.out::println);
        //抛出ClassCastException,Person没有实现Comparable接口
        
        //定制排序,按年龄排
        List<Person> peoples = getData.getArrayList();
        peoples.stream().sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge())).forEach(System.out::println);
        
  9. Stream的终止操作

    1. 匹配与查找

      1. allMatch(Predicate p):检查是否匹配所有元素

      2. anyMatch(Predicate p):检查是否至少匹配一个元素

      3. noneMatch(Predicate p):检查是否没有匹配所有元素

      4. findFirst():返回第一个元素

      5. findAny():返回当前流中的任意元素

      6. count():返回流中元素总数

      7. max(Comparator c):返回流中最大值

      8. min(Comparator c):返回流中最小值

      9. forEach(Consumer c):内部迭代

        使用 Collection 接口需要用户去做迭代, 称为外部迭代,

        Stream API 使用内部迭代——它帮你把迭代做了

    2. 归约

      1. reduce(T iden, BinaryOperator b):可以将流中元素反复结合起来,得到一 个值。返回 T

        List<Integer> list = Arrays.asList(1,2,3,4,5,6);
        Integer sum = list.stream().reduce(0, (x, y) -> Integer.sum(x, y));
        System.out.println(sum);//21
        
      2. reduce(BinaryOperator b):可以将流中元素反复结合起来,得到一 个值。返回 Optional<T>

        List<Person> peoples = getData.getArrayList();
        Stream<Integer> ageStream = peoples.stream().map(p ->p.getAge());
        Optional<Integer> ageSum = ageStream.reduce((x, y) -> Integer.sum(x, y));
        System.out.println(ageSum);
        
    3. 收集

      collect(Collector c):将流转换为其他形式。接收一个 Collector 接口的实现,用于给Stream中元素做汇总 的方法

      List<Person> peoples = getData.getArrayList();
      //收集到List中
      List<Person> personList = peoples.stream().filter(p -> p.getAge() > 20).collect(Collectors.toList());
      personList.forEach(System.out::println);
      
      //收集到Set中
      Set<Person> personSet = peoples.stream().filter(p -> p.getAge() > 20).collect(Collectors.toSet());
      personSet.forEach(System.out::println);
      

15.5 Optional类(减少空指针异常)

  1. Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在

  2. 原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常

  3. Optional提供的方法

    1. 创建Optional类对象的方法:

      1. Optional.of(T t): 创建一个 Optional 实例, t必须非空

        Person person = new Person();
        
        //person = null;//NullPointerException不可为空
        
        Optional<Person> person1 = Optional.of(person);
        System.out.println(person1);
        
      2. Optional.empty(): 创建一个空的 Optional 实例

      3. Optional.ofNullable(T t)t可以为null

        Person person = new Person();
        
        person = null;//可以为空
        
        Optional<Person> person2 = Optional.ofNullable(person);
        System.out.println(person2);//Optional.empty
        
    2. 判断Optional容器中是否包含对象:

      1. boolean isPresent(): 判断是否包含对象
      2. void ifPresent(Consumer<? super T> consumer) : 如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它
    3. 获取Optional容器的对象:

      1. T get(): 如果调用对象包含值,返回该值,否则抛异常
      2. T orElse(T other): 如果有值则将其返回,否则返回指定的other对象
      3. T orElseGet(Supplier<? extends T> other) : 如果有值则将其返回,否则返回由Supplier接口实现提供的对象
      4. T orElseThrow(Supplier<? extends X> exceptionSupplier) : 如果有值则将其返回,否则抛出由Supplier接口实现提供的异常
  4. 示例:

    //避免空指针异常,不再使用之前的多重判断套嵌
    if(xxx != null){    
    	if(xxx.yy != null){        
    		...    
    	}
    }
    
    @Test
    public void test1() {
    Boy b = new Boy("张三");
    
    //b.getGrilFriend()可能为null
    //故用ofNullable()将b.getGrilFriend()包装成Optional
    Optional<Girl> opt = Optional.ofNullable(b.getGrilFriend());
    
    // 如果女朋友存在就打印女朋友的信息
    opt.ifPresent(System.out::println);
    }
    
    
    @Test
    public void test2() {
    Boy b = new Boy("张三");
    Optional<Girl> opt = Optional.ofNullable(b.getGrilFriend());
    
    // 如果有女朋友就返回他的女朋友,否则只能欣赏“嫦娥”了
    Girl girl = opt.orElse(new Girl("嫦娥"));
    System.out.println("他的女朋友是: " + girl.getName());
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值