博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

JavaSE - 16JDK8新特性2

Posted on 2020-12-11 10:46  Kingdomer  阅读(468)  评论(0编辑  收藏  举报

JavaSE - 16JDK8新特性2

(5)强大的Stream API

(5.1)StreamAPI说明

  • Java8中两大最为重要的改变,一个是Lambda表达式;另一个就是Stream API
  • StreamAPI(java.util.stream)把真正的函数式编程风格引入到Java中,让程序员可以写出高效、干净、简洁的代码。
  • StreamAPI是Java8中处理集合的关键抽象概念,可以指定希望对集合进行的操作,也可以执行非常复杂的查找、过滤和映射数据等操作。
    • 使用StreamAPI对集合数据进行操作,类似于使用SQL执行的数据库查询。也可以使用StreamAPI来并行执行操作。
  • Collection 和 Stream集合的区别: Collection是一种静态的内存数据结构,而Stream是有关计算的。
    • 前者是主要面向内存, 存储在内存中,后者主要是面向CPU, 通过CPU实现计算。

(5.2)什么是Stream

  • Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。 集合讲的是数据,Stream讲的是计算。
  • Stream 自己不会存储元素。
  • Stream 不会改变源对象。相反, 他们会返回一个持有结果的新Stream。
  • Stream 操作是延迟执行的。 这意味着他们会等到需要结果的时候才执行。
  • Stream 操作步骤:
    • 创建Stream: 一个数据源(如:集合、数组),获取一个流
    • 中间操作:    一个中间操作链,对数据源的数据进行处理
    • 终止操作:    一旦执行终止操作,就执行中间操作链,并产生结果通知。之后,不会再被使用。

(5.3)Stream 创建

(5.3.1)创建Stream方式一: 通过集合

  • Java 8中的Collection 接口被扩展,提供了两个获取流的方法:
    • default Stream<E> stream(): 返回一个顺序流
    • default Stream<E> parallelStream(): 返回一个并行流
        List<Employee> employees = EmployeeData.getEmployees();
        // default Stream<E> stream(): 返回一个顺序流
        Stream<Employee> stream = employees.stream();
        // parallelStream(): 返回一个并行流
        Stream<Employee>  parallelStream = employees.parallelStream();

(5.3.2)创建Stream方式二: 通过数组

        // 调用Arrays类的 static <T> Stream<T> stream(T[] array): 返回一个流
        int[] arr = new int[]{1,2,3,45,6};
        IntStream stream2 = Arrays.stream(arr);

        Employee e1 = new Employee(1,"Tom");
        Employee e2 = new Employee(2,"Jack");
        Employee[] emp = new Employee[]{e1,e2};
        Stream<Employee> stream3 = Arrays.stream(emp);

(5.3.3)创建Stream方式三: 通过Stream的of()

        Stream<Integer> stream4 = Stream.of(1,2,3,4,5,6);
        Stream<Employee> stream5 = Stream.of(e1,e2);

(5.3.4)创建Stream方式四: 创建无限流

        // 迭代:public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
        Stream.iterate(0, t -> t+2).forEach(System.out::println);
        // 遍历前10个偶数
        Stream.iterate(0, t -> t+2).limit(10).forEach(System.out::println);

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

(5.4)Stream 中间操作

  • 多个中间操作可以连接起来形成一个流水线, 除非流水线上触发终止操作,否则中间操作不会执行任何的处理。
  • 在终止操作时一次性全部处理,称为"惰性求值"。
(5.4.1)筛选与切片
  • filter(Predicate p):  接收Lambda, 从流中排除某些元素
  • distinct():           筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
  • limit(long maxSize):  截断流, 使其元素不超过给定数量
  • skip(long n):         跳过元素,返回一个去掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补。

        List<Employee> employees = EmployeeData.getEmployees();
        Stream<Employee> stream = employees.stream();
        stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);

        stream.limit(3).forEach(System.out::println);

        stream.skip(3).forEach(System.out::println);

        stream.distinct().forEach(System.out::println);

(5.4.2)映射 

  • map(Function f):                 接收一个函数作为参数,该参数会被应用到每个元素上,并将其映射成一个新的元素。
  • mapToDouble(ToDoubleFunction f): 接收一个函数作为参数,该参数会被应用到每个元素上,产生一个新的DoubleStream。
  • mapToInt(ToIntFunction f):       接收一个函数作为参数,该参数会被应用到每个元素上,产生一个新的IntStream。
  • mapToLong(ToLongFunction f):     接收一个函数作为参数,该参数会被应用到每个元素上,产生一个新的LongStream。
  • flatMap(Function f):             接收一个函数作为参数,将流中的每个值都转换成另一个流,然后把所有的流连接成一个流。
        List<String> list = Arrays.asList("aa","vv","cc","dd");
        list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);

        List<Employee> employees = EmployeeData.getEmployees();
        Stream<Employee> stream = employees.stream();
        Stream<String> nameStream = stream.map(Employee::getName);
        nameStream.filter(name -> name.length() > 3).forEach(System.out::println);

        Stream<Stream<Character>> streamStream = list.stream().map(StreamTest::fromStringToStream);
        streamStream.forEach(s -> {
            s.forEach(System.out::println);
        });

        Stream<Character> characterStream = list.stream().flatMap(StreamTest::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();
    }

(5.4.3)排序

        // sorted():自然排序
        List<Integer> list = Arrays.asList(12,43,65,34,87,1,-9,19);
        list.stream().sorted().forEach(System.out::println);
        // sorted(Comparator com): 定制排序
        List<Employee> employees = EmployeeData.getEmployees();
        employees.stream().sorted((e1,e2) ->{return 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);

 

 

(5.5)Stream 终止操作

  • 终端操作会从流的流水线生成结果。其结果可以是 任何不是流的值,如List、Integer,甚至是 void。
  • 流进行了终止操作后,不能再次使用。

(5.5.1)匹配与查找

  • allMatch(Predicate p):  检查是否匹配所有元素
  • anyMatch(Predicate p):  检查是否至少匹配一个元素
  • noneMatch(Predicate p): 检查是否没有匹配所有元素
  • findFirst():            返回第一个元素
  • findAny():              返回当前流中的任意元素
        //  是否所有的员工年龄都大于18
        boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
        System.out.println(allMatch);
        // 是否有任意一个员工的工资大于10000
        boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
        System.out.println(anyMatch);
        // 是否所有员工都不姓"雷",
        boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
        System.out.println(noneMatch);
        // 返回第一个元素
        Optional<Employee> firstEmp = employees.stream().findFirst();
        System.out.println(firstEmp);
        // 返回任意元素
        Optional<Employee> anyEmp = employees.parallelStream().findAny();
        System.out.println(anyEmp);

 

        // 返回最大值
        Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
        Optional<Double> maxSalary = salaryStream.max(Double::compare);
        System.out.println(maxSalary.get());  //Optional[9865.38]   9865.38
        // 返回最小值
        Optional<Employee> minSalaryEmp = employees.stream().min((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary()));
        System.out.println(minSalaryEmp); //Optional[Employee{id=1008, name='扎克伯格', age=35, salary=2500.38}]
        // forEach(Consumer c): 内部迭代
        employees.stream().forEach(System.out::println);

(5.5.2)归约

  • reduce(T iden, BinaryOperator b):  可以将流中元素反复结合起来,得到一个值。返回T
  • reduce(BinaryOperator b):          可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
  • 备注: map 和reduce 的连接通常称为 map-reduce模式,因Google用它进行网络搜索而出名。
 
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        int sum  = list.stream().reduce(0,Integer::sum);  
        System.out.println(sum); // 55

        List<Employee> employees = EmployeeData.getEmployees();
        Optional<Double> salarySum = employees.stream().map(e -> e.getSalary()).reduce(Double::sum);
        System.out.println(salarySum); // Optional[48675.03999999999]
        Optional<Double> salarySum2 = employees.stream().map(Employee::getSalary).reduce((d1,d2) -> d1+d2);
        System.out.println(salarySum2); // Optional[48675.03999999999]

 (5.5.2)收集

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

Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)。

另外,Collectors实用类提供了很多静态方法,可以创建常见收集器实例。

方法 返回类型 作用
toList List<T> 把流中元素收集到List
toSet Set<T> 把流中元素收集到Set
toCollection Collection<T> 把流中元素收集到创建的集合
counting Long 计算流中元素的个数
summingInt Integer 对流中元素的整数属性求和
avaragingInt Double 计算流中元素Integer属性的平均值
summarizingInt IntSummaryStatistics 收集流中Integer属性的统计值。如平均值、最大值、最小值、总数量

 

 

 

 

 

 

 

 

 

 

 

        List<Employee> employees = EmployeeData.getEmployees();
        List<Employee> empList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
        empList.forEach(System.out::println);

        Set<Employee> empSet= employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
        empSet.forEach(System.out::println);

 

 

 

 

 

        Collection<Employee> emps = employees.stream().collect(Collectors.toCollection(ArrayList::new));
        System.out.println(emps);
        long count = employees.stream().collect(Collectors.counting());
        System.out.println(count); // 8
        double totalSalary = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(totalSalary); //48675.04
        double avgSalary = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avgSalary); //6084.38
        DoubleSummaryStatistics dss = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(dss.getMax() + "--" + dss.getMin() + "--" + dss.getAverage()); //9865.38--2500.38--6084.38

 

 

 

 

 

(6)Optional类

 

  • 为了解决空指针异常,Google公司的Guava项目引入了Optional类,Guava通过使用检查空值的方法来防止代码污染。
    • 受Google Guava启发,Optional类已经成为Java 8类库的一部分。
  • Optional<T> 类(java.util.Optional)是一个容器类,可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。
    • 原来用null表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异常。
  • Optional类的JavaDoc描述如下: 这是一个可以为null的容器对象。
    • 如果值存在则isPresent()方法会返回true, 调用get()方法会返回该对象。

(6.1)Optional类方法

 

  • 创建Optional类对象的方法:
    • Optional.of(T t):         创建一个 Optional实例,t 必须非空
    • Optional.empty():         创建一个空的Optional实例
    • Optional.ofNullable(T t): t 可以为null
  • 判断Optional 容器中是否包含对象:
    • boolean isPresent():      判断是否包含对象
    • void ifPresent(Consumer<? super T> consumer): 如果有值,执行Consumer接口的实现代码,并且该值会作为参数传给它。
  • 获取Optional容器的对象
    • T get():                  如果调用对象包含值,返回该值,否则抛异常
    • T orElse(T other):        如果有值则将其返回,否则返回指定的other对象
    • T orElseGet(Supplier<? extends T> other):  如果有值将其返回,否则返回由Supplier接口实现提供的对象。
    • T orElseThrow(Supplier<? extends X> exceptionSupplier): 如果有值将其返回,否则抛出由Supplier接口实现提供的异常。

 

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

        Girl girl2 = new Girl();
        girl2 = null;
        Optional<Girl> optionalGirl2 = Optional.ofNullable(girl2);

        Girl girl3 = optionalGirl2.orElse(new Girl("春天",11));
        System.out.println(girl3); 
        //  Girl{name='null', age=0, boy=null}   未添加 girl2 =null时
        //  Girl{name='春天', age=11, boy=null} 

 

未做判断时: 报空指针异常

 

    public String getGirlName(Boy boy){
        return boy.getGirl().getName();
    }

    @Test
    public void test2(){
        Boy boy = new Boy();
        String girlName = getGirlName(boy);  // java.lang.NullPointerException
        System.out.println(girlName);
    }

 

 

 

做判断: 返回null

 

    public String getGirlName(Boy boy){
//        return boy.getGirl().getName();

        if(boy != null){
            Girl girl = boy.getGirl();
            if(girl != null){
                return girl.getName();
            }
        }
        return null;
    }

 

 

 

使用Optional 实现

 

    public String getGirlName2(Boy boy){
        Optional<Boy> boyOptional = Optional.ofNullable(boy);
        Boy boy2 = boyOptional.orElse(new Boy(new Girl("地理",23)));
        Girl girl = boy2.getGirl();
        Optional<Girl> girlOptional = Optional.ofNullable(girl);
        Girl girl2 =  girlOptional.orElse(new Girl("古老",12));
        return girl2.getName();
    }
// boy=null时 boy2将Girl进行赋值; boy=new Boy()时 boy非空,到了 girl2时对Girl进行赋值 @Test
public void test3(){ Boy boy = null; boy = new Boy(); String girlName = getGirlName2(boy); System.out.println(girlName); // boy=null: 地理 boy=new Boy():古老 }

 

 

 

 

 

 

        Optional<Object> op1 = Optional.empty();
        if(!op1.isPresent()){
            System.out.println("数据为空");   // 数据为空
        }
        System.out.println(op1);             // Optional.empty
        System.out.println(op1.isPresent()); // false
        System.out.println(op1.get());       // java.util.NoSuchElementException: No value present

 

 

 

 

 

 

 

        String str = "hello";
        str = null;
        // of()要求t非空,如t为空,get()方法报错
        Optional<String> op2 = Optional.of(str);
        System.out.println(op2.get());  // hello     加入str=null: java.lang.NullPointerException

 

 

 

 

 

 

        String str2 = "beijn";
        str2 = null;
        Optional<String> op3 = Optional.ofNullable(str2);
        String str3 = op3.orElse("shanho");
        System.out.println(str3);             // beijn  加入str2=null: shanho