栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JavaEE基础第16章Java8新特性

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JavaEE基础第16章Java8新特性

第16章Java8新特性 新特性简介

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

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。 Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。

Lambda表达式 举例:
public class LambdaTest {
    @Test
    public void test1(){
        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("我爱北京天安门");
            }
        };
        runnable1.run();
    }

    //Lambda表达式
    @Test
    public void test2(){
        Runnable runnable2 = () -> System.out.println("我爱北京故宫");
        runnable2.run();
    }

    @Test
    public void test3(){
        Comparator com1 = new Comparator() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
        int compare1 = com1.compare(12, 21);
        System.out.println(compare1);

        //Lambda表达式的写法
        Comparator com2 = (o1,o2) -> Integer.compare(o1,o2);
        int compare2 = com2.compare(32, 21);
        System.out.println(compare2);

        //方法引用的写法
        Comparator com3 = Integer :: compare;
        int compare3 = com3.compare(32, 21);
        System.out.println(compare3);
    }
}

使用

Lambda表达式的使用

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

2.格式:

​ -> :Lambda操作符 或 箭头操作符

​ ->左边:Lambda形参列表 (其实就是接口中的抽象方法的形参列表)

​ ->右边:Lambda体 (其实就是重写的抽象方法的方法体)

3.Lambda表达式的使用:(分为6种情况介绍)

总结:

->左边:Lambda形参列表的参数类型可以省略(类型推断);如果Lambda形参列表只有一个参数,其一对()也可以省略。

->右边:Lambda体应该使用一对 {} 包裹;如果Lambda体只有一条执行语句(可能是return语句),省略这一对{} 和return关键字

4.Lambda表达式的本质:作为函数式接口的实例

5.如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

6.所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

7.在java.util.function包下定义了Java 8 的丰富的函数式接口。

6种情况举例:

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

        Runnable runnable2 = () -> {
            System.out.println("我爱北京故宫");//作为接口的实例。万事万物皆对象
        };
        runnable2.run();
    }

    //语法格式二:Lambda 需要一个参数,但是没有返回值。
    @Test
    public void test5(){
        Consumer con = new Consumer() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("谎言和誓言的区别是什么呢?");

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

    //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
    @Test
    public void test6(){
        Consumer con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("一个是听的人当真了,一个是说的人当真了");
        //前面泛型对类型已经进行了规范,所以类型就已经确定下来了
        Consumer con2 = (s) -> {
            System.out.println(s);
        };
        con2.accept("一个是听的人当真了,一个是说的人当真了");

        //类型推断在之前的应用
        ArrayList list = new ArrayList<>();//类型推断
        int[] arr = {1,2,3};//类型推断
    }

    //语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
    @Test
    public void test7(){
        Consumer con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("一个是听的人当真了,一个是说的人当真了");
        //前面泛型对类型已经进行了规范,所以类型就已经确定下来了
        Consumer con2 = s -> {
            System.out.println(s);
        };
        con2.accept("一个是听的人当真了,一个是说的人当真了");

    }

    //语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
    @Test
    public void test8(){
        Comparator com1 = new Comparator() {
            @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 com2 = (o1,o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };
        System.out.println(com2.compare(32,21));
    }

    //语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
    @Test
    public void test9(){
        Comparator com2 = (o1,o2) -> {
            return o1.compareTo(o2);
        };
        System.out.println(com2.compare(32,21));

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

        Consumer con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("一个是听的人当真了,一个是说的人当真了");
        //只有一条执行语句的时候,大括号才可以省略
        Consumer con2 = (String s) -> System.out.println(s);
        con2.accept("一个是听的人当真了,一个是说的人当真了");
    }

自定义函数式接口:

//自定义函数式接口
@FunctionalInterface
public interface MyInterface {
    void method1();
}
函数式接口

以前用匿名实现类表示的现在都可以用Lambda表达式来写,当然还用之前的匿名实现类也是可以的。

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

类型接口方法
消费型接口Consumervoid accept(T t)
供给型接口SupplierT get()
函数型接口FunctionR apply(T t)
断定型接口Predicateboolean test(T t)

举例:

//使用函数式接口举例一
@Test
public void test10(){
    happyTime(500, new Consumer() {
        @Override
        public void accept(Double aDouble) {
            System.out.println("吃一顿饭花费为" + aDouble);
        }
    });

    //使用Lambda表达式
    happyTime(400,aDouble -> System.out.println("吃一顿饭花费为" + aDouble));
}

private void happyTime(double money,Consumer con){
    con.accept(money);
}

//使用函数式接口举例二
@Test
public void test11(){
    List list = Arrays.asList("北京","南京","东京","天津");
    List filterStrs = filetString(list, new Predicate() {
        @Override
        public boolean test(String s) {
            return s.contains("京");
        }
    });
    System.out.println(filterStrs);

    //Lambda表达式
    List filterStrs1 = filetString(list,s -> s.contains("京"));
    System.out.println(filterStrs1);
}

//根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定。
private List filetString(List list, Predicate pre){
    ArrayList filterList = new ArrayList<>();
    for (String s:
         list) {
        if (pre.test(s)){
            filterList.add(s);
        }
    }
    return filterList;
}

出现以上接口,都可以用Lambda表达式去写,可以不用匿名实现类了。

何时使用Lambda表达式?

当需要对一个函数式接口实例化的时候,可以使用lambda表达式。

何时使用给定的函数式接口?

如果我们开发中需要定义一个函数式接口,首先看看在已有的jdk提供的函数式接口是否提供了能满足需求的函数式接口。如果有,则直接调用即可,不需要自己再自定义了。

方法引用

方法引用的使用

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

2.方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以方法引用,也是函数式接口的实例。

3.使用格式: 类(或对象) :: 方法名 【注意这里只有方法名,不需要参数列表】

4.具体分为如下的三种情况:

​ 情况一 对象 :: 非静态方法

​ 情况二 类 :: 静态方法

​ 情况三 类 :: 非静态方法 (注意这里跟面向对象那部分有些区别,这里类是可以调非静态方法的)

5.方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同!(针对于情况一和情况二)。而情况三也有一定的规律,即当第一个参数作为调用者时可以考虑方法引用。

public class MethodRefTest {

   // 情况一:对象 :: 实例方法
   //Consumer中的void accept(T t)
   //PrintStream中的void println(T t)
   @Test
   public void test1() {
      //Lambda表达式
      Consumer con1 = s -> System.out.println(s);
      con1.accept("北京");

      //方法引用  对象 :: 非静态方法
      PrintStream ps = System.out;
      Consumer con2 = ps :: println;
      con2.accept("beijing");
   }
   
   //Supplier中的T get()
   //Employee中的String getName()
   @Test
   public void test2() {
      //Lambda表达式
      Employee employee = new Employee(1001,"Tom",23,5600);
      Supplier sup1 = () -> employee.getName();
      System.out.println(sup1.get());

      //方法引用  对象 :: 非静态方法
      Supplier sup2 = employee::getName;
      System.out.println(sup2.get());
   }

   // 情况二:类 :: 静态方法
   //Comparator中的int compare(T t1,T t2)
   //Integer中的int compare(T t1,T t2)
   @Test
   public void test3() {
      //Lambda表达式
      Comparator com1 = (t1,t2) -> Integer.compare(t1,t2);
      System.out.println(com1.compare(12,21));

      //方法引用   类 :: 静态方法
      Comparator com2 = Integer :: compare;
      System.out.println(com2.compare(12,21));
   }
   
   //Function中的R apply(T t)
   //Math中的Long round(Double d)
   @Test
   public void test4() {
      //匿名类
      Function fun1 = new Function() {
         @Override
         public Long apply(Double d) {
            return Math.round(d);
         }
      };
      System.out.println(fun1.apply(5.4));

      //Lambda表达式
      Function fun2 = d -> Math.round(d);
      System.out.println(fun2.apply(6.5));

      方法引用   类 :: 静态方法
      Function fun3 = Math::round;
      System.out.println(fun3.apply(8.5));
   }

   // 情况三:类 :: 实例方法
   //情况三虽然形参列表不对应了,但是也有一定的规律。第一个参数作为调用者时可以考虑方法引用
   // Comparator中的int comapre(T t1,T t2)
   // String中的int t1.compareTo(t2)
   @Test
   public void test5() {
      //Lambda表达式
      Comparator com1 = (s1,s2) -> s1.compareTo(s2);
      System.out.println(com1.compare("abc","abd"));

      //方法引用 类 :: 实例方法
      Comparator com2 = String::compareTo;
      System.out.println(com2.compare("abc","abm"));
   }

   //BiPredicate中的boolean test(T t1, T t2);
   //String中的boolean t1.equals(t2)
   @Test
   public void test6() {
      //Lambda表达式
      BiPredicate pre1 = (s1,s2) -> s1.equals(s2);
      System.out.println(pre1.test("abc","abc"));
      //方法引用 类 :: 实例方法
      BiPredicate pre2 = String::equals;
      System.out.println(pre2.test("abc","abd"));
   }
   
   // Function中的R apply(T t)
   // Employee中的String getName();
   @Test
   public void test7() {
      Employee employee = new Employee(1001,"Jerry",23,6000);
      //Lambda表达式
      Function fun1 = e -> e.getName();
      System.out.println(fun1.apply(employee));

      //方法引用 类 :: 实例方法
      Function fun2 = Employee::getName;
      System.out.println(fun2.apply(employee));
   }

}

总结:当可以使用方法引用的时候可以使用方法引用,再者考虑Lambda表达式,如果这两个都不会则只能用匿名类。

使用建议:如果给函数式接口提供实例,恰好满足方法引用的使用情境,就可以考虑使用方法引用给函数式接口提供实例。如果不熟悉方法引用,那么还可以使用Lambda表达式。

构造器引用

一、构造器引用

​ 格式: 类名 :: new

​ 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。抽象方法的返回值类型即为构造器所属的类的类型。

二、数组引用

​ 格式: 数组类型[] :: new

​ 大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。

public class ConstructorRefTest {
   //构造器引用
    //Supplier中的T get()
    //Employee的空参构造器:Employee()
    @Test
    public void test1(){
        //匿名类
        Supplier sup1 = new Supplier() {
            @Override
            public Employee get() {
                return new Employee();
            }
        };

        //Lambda表达式
        Supplier sup2 = () -> new Employee();

        //构造器引用
        Supplier sup3 = Employee::new;

    }

   //Function中的R apply(T t)
    //Employee的构造器:Employee(id)
    @Test
    public void test2(){
        Function func1 = new Function() {
            @Override
            public Employee apply(Integer integer) {
                return new Employee(integer);
            }
        };
        //Lambda表达式
        Function func2 = id -> new Employee(id);
        func2.apply(1001);
        //构造器引用
        Function func3 = Employee::new;
        func3.apply(1002);
    }

   //BiFunction中的R apply(T t,U u)
    //Employee的构造器:Employee(id,name)
    @Test
    public void test3(){
        BiFunction func1 = new BiFunction() {
            @Override
            public Employee apply(Integer integer, String s) {
                return new Employee(integer,s);
            }
        };

        //Lambda表达式
        BiFunction func2 = (id,name) -> new Employee(id,name);
        func2.apply(1001,"Jerry");

        //构造器引用
        BiFunction func3 = Employee::new;
        func3.apply(1002,"Tom");
   }

   //数组引用
    //Function中的R apply(T t)
    @Test
    public void test4(){
        Function func1 = new Function() {
            @Override
            public String[] apply(Integer integer) {
                return new String[integer];
            }
        };

        //Lambda表达式
        Function func2 = length -> new String[length];
        String[] arr1 = func2.apply(10);
        System.out.println(Arrays.toString(arr1));

        //数组引用
        Function func3 = String[]::new;
        String[] arr2 = func3.apply(5);
        System.out.println(Arrays.toString(arr2));
    }
}
Stream API

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

Stream 和 Collection 集合的区别:

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

集合讲的是数据,Stream讲的是计算!

注意:

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

Stream 的操作三个步骤:

1- 创建 Stream

​ 一个数据源(如:集合、数组),获取一个流

2- 中间操作

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

3- 终止操作(终端操作)

​ 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用。

所谓延迟执行,就是只有在执行终止操作后,一系列的中间操作才会开始执行,且执行完毕就会终止,不可再被使用。

概述

1.Stream关注的是对数据的运算,与CPU打交道

​ 集合关注的是数据的存储,与内存打交道

​ 就是java8提供了一套API,使用这套API可以对内存中的数据进行过滤、排序、映射、归约等操作。类似于sql对数据库中表的相关操作。

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

    3.Stream执行流程

    ①Stream的实例化

    ②一系列中间操作(过滤、映射、…)

    ③终止操作

    4.说明

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

    ②一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用

    Stream的实例化

    四种方式,主要看前三种。需要集合就用第一种,需要数组就第二种,需要直接创建一个流就用第三种,需要生成一些数据就用第四种。

    //测试Stream的实例化
    public class StreamAPITest {
        @Test
        public void test1(){
            //创建 Stream方式一:通过集合
            List employees = EmployeeData.getEmployees();
    
            //default Stream stream() : 返回一个顺序流
            Stream stream = employees.stream();
    
            //default Stream parallelStream() : 返回一个并行流
            Stream employeeStream = employees.parallelStream();
        }
    
        @Test
        public void test2(){
            //创建 Stream方式二:通过数组
            //调用Arrays类的static  Stream stream(T[] array): 返回一个流
            int[] arr1 = new int[]{1,2,3,4,5};
            IntStream stream = Arrays.stream(arr1);
    
            Employee e1 = new Employee(1001,"Jerry");
            Employee e2 = new Employee(1002,"Tom");
            Employee[] arr2 = new Employee[]{e1,e2};
            Stream stream1 = Arrays.stream(arr2);
    
        }
    
        @Test
        public void test3(){
            //创建 Stream方式三:通过Stream的of()
            //public static Stream of(T... values) : 返回一个流
            //可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
            Stream stream = Stream.of(1, 2, 3, 4, 5, 6);
        }
    
        @Test
        public void test4(){
            //创建 Stream方式四:创建无限流.作用就是用来造数据
            //可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
            //迭代    public static Stream iterate(final T seed, final UnaryOperator f)
            Stream.iterate(0,t -> t + 2).limit(10).forEach(System.out::println);//遍历前10个偶数
            //生成    public static Stream generate(Supplier s)
            Stream.generate(Math::random).limit(10).forEach(System.out::println);
        }
    }
    

    在情况四中," .forEach() "相当于消费者,用来执行终止操作,只有执行了终止操作,中间操作才会开始执行。

    Stream中间操作 筛选与切片

    filter(Predicate p)——接收Lambda,从流中排除某些元素。

    limit(n)——截断流,使其元素不超过给定数量。

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

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

    @Test
    public void test5(){
        //1.筛选与切片
        //filter(Predicate p)——接收Lambda,从流中排除某些元素。
        List list = EmployeeData.getEmployees();
        Stream stream = list.stream();
        //练习查询员工表中薪资大于7000的员工信息
        stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);//forEach相当于终止操作
    
        //limit(n)——截断流,使其元素不超过给定数量。
        //一定要重新生成一个流,因为执行完终止操作后就结束了,不能再去执行中间操作了,所以需要重新生成一个流
        //类似于迭代器Iterator,每次使用都要重新生成
        list.stream().limit(3).forEach(System.out::println);
    
        //skip(n)——跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流。与limit(n)互补
        list.stream().skip(3).forEach(System.out::println);
    
        //distinct()——筛选,通过流所生成元素的hashCode() 和 equals() 去除重复元素
        list.stream().distinct().forEach(System.out::println);
    }
    
    映射

    映射的第二个比较难理解,需要及时复习。

        @Test
        public void test6(){
            //2.映射
            //map(Function f)——接收一个函数作为参数,将元素转换为其他形式或提取信息,该函数会被应用到
            //每个元素上,并将其映射成一个新的元素。
            List list = Arrays.asList("aa", "bb", "cc");
            list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
    
            //练习1:获取员工姓名长度大于3的员工的姓名
            List employees = EmployeeData.getEmployees();
    //        Stream namesStream = employees.stream().map(e -> e.getName());//Lambda表达式
            Stream namesStream = employees.stream().map(Employee::getName);//方法引用
            namesStream.filter(name -> name.length() > 3).forEach(System.out::println);
    
            //练习2:遍历每一个元素
            Stream> streamStream = list.stream().map(StreamAPITest::fromStringToStream);
            streamStream.forEach(s -> s.forEach(System.out::println));
    
            //flatMap(Function f)——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
            Stream characterStream = list.stream().flatMap(StreamAPITest::fromStringToStream);
            characterStream.forEach(System.out::println);//结果与上面两层嵌套是一样的
        }
    
        public static Stream fromStringToStream(String str){
            ArrayList list = new ArrayList<>();
            for (Character c :
                    str.toCharArray()) {
                list.add(c);
            }
            return list.stream();
        }
    
        @Test
        public void test7(){
            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);
    
            list1.add(list2);//相当于map(Function f)
            list1.addAll(list2);//相当于flatMap(Function f)
            System.out.println(list1);
        }
    
    排序

    在Java层面涉及到排序就去想Comparable和Comparator。

        @Test
        public void test8(){
            //3.排序  在Java层面涉及到排序就去想Comparable和Comparator
            //sorted()——自然排序
            List list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7);
            list.stream().sorted().forEach(System.out::println);
            //抛异常,原因:Employee没有实现Comparable接口
    //        List employees = EmployeeData.getEmployees();
    //        employees.stream().sorted().forEach(System.out::println);
    
            //sorted(Comparator com)——定制排序
            List employees = EmployeeData.getEmployees();
            employees.stream().sorted(new Comparator() {
                @Override
                public int compare(Employee o1, Employee o2) {
                    int agevalue = Integer.compare(o1.getAge(),o2.getAge());
                    if (agevalue != 0){
                        return agevalue;
                    }else {
                        return Double.compare(o1.getSalary(),o2.getSalary());
                    }
                }
            }).forEach(System.out::println);
            //Lambda表达式实现
            employees.stream().sorted((o1,o2) -> {
                int agevalue = Integer.compare(o1.getAge(),o2.getAge());
                if (agevalue != 0){
                    return agevalue;
                }else {
                    return Double.compare(o1.getSalary(),o2.getSalary());
                }
            }).forEach(System.out::println);
        }
    
    Stream终止操作 查找与匹配
    @Test
    public void test9(){
        List 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);
    
        //noneMatch(Predicate p)    检查是否没有匹配的元素
        //练习:是否存在员工姓“雷”
        boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
        System.out.println(noneMatch);//注意该方法检查的 是否没有匹配的元素。
    
        //findFirst()   返回第一个元素
        Optional employee = employees.stream().findFirst();
        System.out.println(employee);
    
        //findAny() 返回当前流中的任意元素
        Optional any = employees.stream().findAny();
        System.out.println(any);
    
        //count()   返回流中元素总数
        long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
        System.out.println(count);
    
        //max(Comparator c) 返回流中最大值
        //练习,返回最高的工资
        Stream doubleStream = employees.stream().map(e -> e.getSalary());//全部映射为薪水流
        doubleStream.max(new Comparator() {
            @Override
            public int compare(Double o1, Double o2) {
                return Double.compare(o1,o2);
            }
        });//匿名类
        doubleStream.max(((o1, o2) -> Double.compare(o1,o2)));//Lambda表达式
        Optional max = doubleStream.max(Double::compare);//方法引用
        System.out.println(max);
    
        //min(Comparator c) 返回流中最小值
        //练习:返回工资最低的员工
        Optional min = employees.stream().min(((o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary())));
        System.out.println(min);
    
        //forEach(Consumer c)   内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭
        //代——它帮你把迭代做了)
        employees.stream().forEach(System.out::println);//这个是stream中的
        //Collection接口中的Iterator迭代器就是外部迭代
    
        employees.forEach(System.out::println);//注意这是使用的是集合的遍历操作,与上面的不同。
    }
    
    归约

    归约就是一串数据,先求出前两个数的和,再将这个和与第三个数求和,如此重复操纵便可求出总和。(归约,将两个归成一个)

        @Test
        public void test10(){
            //归约 :求和即是归约
            //reduce(T identity, BinaryOperator b)——可以将流中元素反复结合起来,得到一个值。返回 T
            //练习1:计算1-10的自然数的和
            List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            Integer sum = list.stream().reduce(0, Integer::sum);//第一个参数是起始值
            System.out.println(sum);
    
            //reduce(BinaryOperator b)——可以将流中元素反复结合起来,得到一个值。返回 Optional
            //练习2:计算公司所有员工工资的总和
            List employees = EmployeeData.getEmployees();
    //        Stream salaryStream = employees.stream().map(e -> e.getSalary());
            Stream salaryStream = employees.stream().map(Employee::getSalary);
    //        Optional sumMoney = salaryStream.reduce(Double::sum);
            Optional sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2);
            System.out.println(sumMoney);
        }
    
    收集

    Collector需要使用Collectors提供实例。

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

    Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、 Map)。
    另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

    Optional类

    Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。

    Optional类:为了在程序中避免出现空指针异常而创建的。

    常用的方法:ofNullable(T t)

    ​ orElse(T t)

    创建Optional类对象的方法:

    Optional.of(T t) : 创建一个 Optional 实例,t必须非空;Optional.empty() : 创建一个空的 Optional 实例Optional.ofNullable(T t):t可以为null.

    判断Optional容器中是否包含对象:

    boolean isPresent() : 判断是否包含对象void ifPresent(Consumer consumer) :如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。

    获取Optional容器的对象:

    T get(): 如果调用对象包含值,返回该值,否则抛异常T orElse(T other) :如果有值则将其返回,否则返回指定的other对象。T orElseGet(Supplier other) :如果有值则将其返回,否则返回由Supplier接口实现提供的对象。T orElseThrow(Supplier exceptionSupplier) :如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。

    public class OptionalTest {
        @Test
        public void test1(){
            Girl girl = new Girl();
    //        girl = null;
            //of(T t):保证t是非空的
            Optional optionalGirl = Optional.of(girl);
            System.out.println(optionalGirl);
        }
    
        @Test
        public void test2(){
            Girl girl = new Girl();
            girl = null;
            //ofNullable(T t):t可以为null
            Optional optionalGirl = Optional.ofNullable(girl);
            System.out.println(optionalGirl);
            
            Girl girl1 = optionalGirl.orElse(new Girl("Jerry"));
            System.out.println(girl1);
        }
    
        public String getGirlName(Boy boy){
            return boy.getGirl().getName();
        }
    
        @Test
        public void test3(){
            Boy boy = new Boy();
            String girlName = getGirlName(boy);
            System.out.println(girlName);
        }
    
        //优化以后的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){
            Optional boyOptional = Optional.ofNullable(boy);
            //此时的boy1一定非空
            Boy boy1 = boyOptional.orElse(new Boy(new Girl("Candy")));
    
            Girl girl = boy1.getGirl();
    
            Optional optionalGirl = Optional.ofNullable(girl);
            //girl1一定非空
            Girl girl1 = optionalGirl.orElse(new Girl("Andy"));
    
            return girl1.getName();
        }
    }
    

    empty():创建的Optional对象内部的value = null 。

    为保证非空,ofNullable(T t) 与orElse (T other) 搭配使用。

    of(T t): 封装数据t生成Optional对象。要求t非空,否则报错。

    如果Optional封装的数据value为空,则get()报错。否则,value不为空时,返回value值。又of(T t) 要求t非空,因此get()通常与of(T t)方法搭配使用。用于获取内部的封装的数据value。

    ofNullable(T t):封装数据t赋给Optional内部的value。不要求t非空。

    orElse(T t1):如果Optional内部的value非空,则返回此value值。如果value为空,则返回t1。

    转载请注明:文章转载自 www.mshxw.com
    本文地址:https://www.mshxw.com/it/731975.html
    我们一直用心在做
    关于我们 文章归档 网站地图 联系我们

    版权所有 (c)2021-2022 MSHXW.COM

    ICP备案号:晋ICP备2021003244-6号