Java8新特性学习笔记

172 阅读6分钟

Java8

1.Lambda表达式引入

  • 策略设计模式:

    实体类Employee

    public class Employee {
        private String name;
    
        private int age;
    
        private double salary;
    }
    

    定义一个接口MyPredicate

    public interface MyPredicate<T> {
        boolean test(T t);
    }
    

    实现类FilterEmployeeByAge继承MyPredicate

    public class FilterEmployeeByAge implements MyPredicate<Employee> {
        //找出获取员工年龄大于等于35的信息
        @Override
        public boolean test(Employee employee) {
            return employee.getAge()>=35;
        }
    }
    

    实现类FilterEmployeeBySalary继承MyPredicate

    public class FilterEmployeeBySalary  implements MyPredicate<Employee>{
        @Override
        //找出获取员工工资大于等于5000的信息
        public boolean test(Employee employee) {
            return employee.getSalary()>=5000;
        }
    }
    

    主方法:

    //填充数据
    List<Employee> employees = Arrays.asList(
        new Employee("张三",42,1000),
        new Employee("李四",31,4000),
        new Employee("王五",25,6000),
        new Employee("赵柳",35,2000),
        new Employee("胡杨",40,10000)
    
    );
    //定义方法
    public List<Employee> filterEmployee(List<Employee> list,MyPredicate<Employee> myPredicate){
        List<Employee> emps = new ArrayList<>();
        for(Employee emp:list){
            // 调用接口中的方法
            if(myPredicate.test(emp)){
                emps.add(emp);
            }
        }
        return emps;
    }
    
    @Test
    public void test(){
    
        List<Employee> emps = filterEmployee(employees,new FilterEmployeeByAge());
    
        for (Employee emp :emps){
            System.out.println(emp);
        }
        System.out.println("-----------");
    
        //改变传入参数的实现类
        List<Employee> emps2 = filterEmployee(employees,new FilterEmployeeBySalary());
    
        for (Employee emp :emps2){
            System.out.println(emp);
        }
    }
    
    //输出
    Employee{name='张三', age=42, salary=1000.0}
    Employee{name='赵柳', age=35, salary=2000.0}
    Employee{name='胡杨', age=40, salary=10000.0}
    -----------
    Employee{name='王五', age=25, salary=6000.0}
    Employee{name='胡杨', age=40, salary=10000.0}
    
  • 策略设计模式缺点:每加一个需求需要加实现类

    • 解决方法:采用匿名内部类

      @Test
      public void test2(){
          List<Employee> emps = filterEmployee(employees, new MyPredicate<Employee>() {
              @Override
              public boolean test(Employee employee) {
                  return employee.getSalary()<=5000;
              }
          });
          for (Employee emp :emps){
              System.out.println(emp);
          }
      }
      
    • 匿名内部类代码冗余,通过Lambda表达式优化

    @Test
    public void test3(){
          List<Employee> emps = filterEmployee(employees, (employee)->employee.getSalary()<=5000);
          emps.forEach(System.out::println);
      }
    
    • 继续优化:接口,方法都不用到 Stream ApI

      @Test
      public void test5(){
          employees.stream()
              .filter((e)->e.getSalary()>=5000)
              .forEach(System.out::println);
      }
      

2.Lambda表达式的基础语法

  • 1.-> 将Lambda拆成两部分:

    左侧:Lambda表达式的参数列表

    右侧:Lambda表达式所需要执行的功能。即lanbda体

  • 2.语法格式:

    • 1.无参数无返回值:() -> System.out.Println("Hello");

      @Test
      public void test(){
          int num =0; //jdk1.7之前,必须是final修饰
          Runnable r = new Runnable(){
              @Override
              public void run() {
                  System.out.println("Hello"+num);
              }
          };
          r.run();
          System.out.println("-----------------");
      
          Runnable r1 =() -> System.out.println("Hello Lambda"+num);
          r1.run();
      }
      //输出
      Hello0
      -----------------
      Hello Lambda0
      
    • 2.有1个参数,并且无返回值:

      @Test
      public void test2(){
          Consumer<String> con=(x) -> System.out.println(x);
          con.accept("迷雾神");
      }
      //输出
      迷雾神
      
    • 3.若只有一个参数,小括号可以不写:

      @Test
      public void test3(){
          //小括号可以不写
          Consumer<String> con=x -> System.out.println(x);
          con.accept("迷雾神");
      }
      
    • 4.有两个参数,并且Lambda有返回值,并且有多条语句:

      @Test
      public void test4(){
          //有多条语句,{}不能省略
          Comparator<Integer>  com= (x,y)->{
              System.out.println("函数式接口");
              return Integer.compare(x, y);
          };
          System.out.println(com.compare(10,9));
      }
      
      
    • 5.有两个参数,并且Lambda有返回值,只有1条语句:

      @Test
      public void test4(){
          //单条语句,return和{}可以省略
          Comparator<Integer>  com= (x,y)->  Integer.compare(x, y);
          System.out.println(com.compare(10,9));
      }
      
    • 6.Lambda的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即“类型推断”。

  • 3.Lambda 表达式需要“函数式接口“的支持

    • 函数式接口:接口中只有一个抽象方法的接口,称为函数式接口
  • 4.练习

    //employees首先按照年龄排序,年龄相同按照姓名排序
    public class LambdaExer {
        List<Employee> employees = Arrays.asList(
            new Employee("张三",22,5000),
            new Employee("李四",32,8000),
            new Employee("王五",23,7000),
            new Employee("赵六",32,6000));
    
        @Test
        public void test1(){
            Collections.sort(employees,(e1,e2)->{
                if(e1.getAge()==e2.getAge()){
                    return e1.getName().compareTo(e2.getName());
                }else{
                    return Integer.compare(e1.getAge(),e2.getAge());
                }
            } );
            employees.forEach(System.out::println);
        }
    }
    

3.Java8 内置的四大核心函数式接口

  • 1.Comsumer : 消费型接口

    void accept(T t);

    @Test
    public void test(){
        happy(10000,(money)-> System.out.println("买了本书,消费了"+money+"元"));
    }
    
    public void happy(double money , Consumer<Double> con){
        con.accept(money);
    }
    
  • 2.Supplier : 供给型接口

    T get();

    @Test
    public void test1(){
        List<Integer> list=getNumList(10,() -> (int)(Math.random()*100) );
        list.forEach(System.out::println);
    
    }
    //需求:产生指定个数的整数,并放入集合中
    public List<Integer> getNumList(int num , Supplier<Integer> sup){
        List<Integer> list=new ArrayList();
        for(int i=0;i<num;i++){
            list.add(sup.get()) ;
        }
        return list;
    }
    
  • 3.Function<T,R> : 函数型接口

    R apply(T t);

    @Test
    public void test2(){
        String newStr= strHandler("\t\t\t 迷雾神 哈哈  ",(str) -> str.trim());
        System.out.println(newStr);
    }
    
    //需求:用于处理字符串
    public String strHandler(String str, Function<String ,String > fun){
        return  fun.apply(str);
    }
    
  • 4.Predicate : 断言型接口

     boolean test(T t);
    
    @Test
    public void test3(){
        List<String> list = Arrays.asList("hello","迷雾神","Lambda","aaa");
        List<String> newList = filterStr(list,(str)->str.length()>3);
        newList.forEach(System.out::println);
    }
    
    //需求:满足条件的字符串放入集合中
    public List<String > filterStr(List<String > lists, Predicate<String > pre){
        List<String> list = new ArrayList();
        for(String str:lists){
            if(pre.test(str)){
                list.add(str);
            }
        }
        return list;
    }
    //输出
    hello
    Lambda
    

4.方法引用

  • 1.若Lambda体中的内容有方法已经实现了,可以使用”方法引用“。

  • 2.三种语法格式:

    • 对象::实例方法名
    • 类::静态方法名
    • 类::实例方法名
    @Test
    public void test(){
        Consumer<String > con = (x) -> System.out.println(x);
        con.accept("迷雾神");
        //方法引用
        PrintStream ps=System.out;
        Consumer<String > con2 = ps::println;
        con2.accept("迷雾神2");
    }
    //输出
    迷雾神
    迷雾神2
    
    @Test
    public void test1(){
        Employee emp = new Employee("张三",22,5000);
        Supplier<String > sup=() -> emp.getName();
        System.out.println(sup.get());
    	// 方法引用
        Supplier<Integer > sup1=emp::getAge;
        System.out.println(sup1.get());
    }
    //输出
    张三
    22
    

5.Stream

  • 1.Stream的创建

    • 1.通过Collection系列集合提供的stream()

      List<String> list =new ArrayList<>();
      Stream<String> stream = list.stream();
      
    • 2.通过Arrays中的静态方法stream()获取数组流

      Employee[] emps = new Employee[10];
      Stream<Employee> stream2 = Arrays.stream(emps);
      
    • 3.通过Stream类中的静态方法of()

      Stream<String> stream3 = Stream.of("aa", "bb", "cc");
      
    • 4.创建无限流

      //迭代
      Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
      //生成
      Stream.generate(()->Math.random());
      
  • 2.筛选与切片

    • 惰性求值:只有当终止操作执行时,才会去执行中间操作
    List<Employee> employees = Arrays.asList(
        new Employee("张三",42,1000),
        new Employee("李四",31,4000),
        new Employee("王五",25,6000),
        new Employee("赵柳",35,2000),
        new Employee("胡杨",40,10000)
    
    );
    @Test
    public void test(){
        //filter
        employees.stream()
            .filter((e) -> e.getAge()>30)
            .forEach(System.out::println);
    
        //limit 只取对应的个数
        employees.stream()
            //找到满足的数量后,就不再执行,短路
            .filter((e) -> e.getSalary()>=4000)
            .limit(2)
            .forEach(System.out::println);
        //输出
        Employee{name='李四', age=31, salary=4000.0}
        Employee{name='王五', age=25, salary=6000.0}	
    
        //skip 跳过对应的个数
        employees.stream()
            .filter((e) -> e.getSalary()>=4000)
            .skip(2)
            .forEach(System.out::println);
        //输出
        //Employee{name='胡杨', age=40, salary=10000.0}
    
        //distinct 去重通过流所生成元素的hashcode()和equals()去除重复元素(要重写hashcode()和equals())
        employees.stream()
            .filter((e) -> e.getSalary()>=4000)
            .distinct ()
            .forEach(System.out::println);
    }
    
  • 3.映射

    • map—接收Lambda ,将元素转换成其他形式或提取信息。接受一个函数作为参数,

      该函数会被应用到每个元素上,并将其映射成一个新的元素。

      List<String> list =Arrays.asList("aa","bb","cc","dd");
      list.stream()
          .map((str) -> str.toUpperCase())
          .forEach(System.out::println);
      //输出
      AA
      BB
      CC
      DD
      
    • flatMap—接受一个函数作为参数,将流中的每一个值都换成另一个流,然后再把所有流连接成一个流

    • @Test
      public void test4(){
      
          List<String> list =Arrays.asList("aa","bb","cc","dd");
      
          list.stream()
              .map(TestStreamApI2::filterStream)
              .forEach((sm) -> {
                  sm.forEach((x) -> System.out.println(x));
              });
      
          list.stream()
              .flatMap(TestStreamApI2::filterStream)
              .forEach(System.out::println);
      }
      
      public static Stream<Character> filterStream(String str){
          List<Character> list = new ArrayList();
          for ( Character ch : str.toCharArray()) {
              list.add(ch);
          }
          return list.stream();
      }
      
  • 4.排序

    • 自然排序 sorted() ----> 自然排序(Comparable)

      @Test
      public void test1(){
          List<String> list =Arrays.asList("bb","cc","aa","dd");
          list.stream().sorted()
              .forEach(System.out::println);
      }
      
    • 定制排序(Comparator com) sorted(Comparator )

          @Test
          public void test1(){
              employees.stream().sorted((e1,e2) ->{
                  if(e1.getAge()==e2.getAge()){
                      return e1.getName().compareTo(e2.getName());
                  }else {
                      return Integer.compare(e1.getAge(), e2.getAge());
                  }
              }).forEach(System.out::println);
          }
      
  • 5.查找与匹配

    • allMatch:是否匹配所有元素

      @Test
      public void test(){
          boolean b= employees.stream()
              .allMatch((e)->e.getStatus().equals(Status.BUSY));
          System.out.println(b);
      }
      
    • anyMatch: 是否至少匹配一个元素

      @Test
      public void test(){
          boolean b= employees.stream()
              .anyMatch((e)->e.getStatus().equals(Status.BUSY));
          System.out.println(b);
      }
      
    • noneMatch: 是否没有匹配所有元素

      @Test
      public void test(){
          boolean b= employees.stream(java)
              .noneMatch((e)->e.getStatus().equals(Status.BUSY));
          System.out.println(b);
      }
      
    • findFirst: 返回第一个元素 (返回值是Optional)

      Optional<Employee> first = employees.stream().findFirst();
      System.out.println(first.get());
      
    • fingAny:返回流中的任意元素

      Optional<Employee> first = employees.stream().fingAny();
      System.out.println(first.get());
      
    • count: 返回流中元素的总个数

      long count = employees.stream().count();
      System.out.println(count);
      
    • max: 返回流中元素最大值

      employees.stream().map(Employee::getSalary).max(Double::compare);
      
    • min:返回流中元素最小值

      employees.stream().map(Employee::getSalary).min(Double::compare);
      
  • 归约与收集

    • reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ---- 可以将流中元素反复结合起来,得到一个值

      @Test
      public void test1(){
      
          List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
      
          Integer sum = list.stream().reduce(0, (x, y) -> x + y);
      
          System.out.println(sum);
      
          System.out.println("--------");
      
          Optional<Double> reduce = employees.stream()
              .map(Employee::getSalary).reduce(Double::sum);
          System.out.println(reduce.get());
      }
      
      //输出
       45
      --------
      41100.0
      
    • collect —将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

      @Test
      public void test2(){
      
          List<String> list = employees.stream()
              .map(Employee::getName)
              .collect(Collectors.toList());
      
          list.forEach(System.out::println);
      }
      
      //Collectors工具提供了很多的方法,可以转换成各种集合的方式。
      
      @Test
      public void test3(){
          //总数
          Long count = employees.stream()
              .collect(Collectors.counting());
          System.out.println(count);
      
          //平均值
          Double avg = employees.stream()
              .collect(Collectors.averagingDouble(Employee::getSalary));
          System.out.println(avg);
      
          //总和
          Double sum = employees.stream()
              .collect(Collectors.summingDouble(Employee::getSalary));
          System.out.println(sum);
      
          //最大值
          Optional<Employee> employee = employees.stream()
              .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
          System.out.println(employee.get());
      }
      

      分组

      @Test
      public void test4(){
          Map<Status, List<Employee>> map = employees.stream()java
              .collect(Collectors.groupingBy(Employee::getStatus));
      }
      

      连接

      @Test
      public void test5(){
          String str = employees.stream()
              .map(Employee::getName)
              .collect(Collectors.joining(","));
          System.out.println(str);
      }
      //张三,赵六,王五,田七,王八,李四