Java8中Stream的用法

1.概述

Stream APl ( java.util.stream)把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。简言之,Stream API提供了一种高效且易于使用的处理数据的方式。

2.特性

Stream是数据渠道,用于操作数据源(数组,集合)所生成的元素序列
集合着重于数据,Stream着重于计算

  • Stream不会自己存储数据
  • Stream不会自己创建对象,它会将操作后的数据保存到另外一个对象中。
  • 惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。

3.Stream操作的三个步骤

  1. 创建Stream
  2. 中间操作
  3. 终止操作

3.1 创建Stream

方式1:通过Collection系列集合提供的stream()或parallelStream()

java stream 属性比较 java stream 用法_java stream 属性比较

// 1.1可以通过Collection系列集合提供的stream()或parallelStream()
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

方式2:通过Arrays中的静态方法stream()获取数组流

java stream 属性比较 java stream 用法_数据库_02

// 1.2通过Arrays中的静态方法stream()获取数组流
        Employee[] emps = new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(emps);

方式3:通过Stream类中的静态方法of()

java stream 属性比较 java stream 用法_System_03

// 1.3通过Stream类中的静态方法of()
        Stream<String> stream3 = Stream.of("aa", "bb", "cc");
        stream3.forEach(System.out::println);

3.2 中间操作

中间操作(Intermediate Operations):中间操作会返回一个新的流,一个流可以后面跟随零个或多个intermediate操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后会返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。而是在终端操作开始的时候才真正开始执行。

java stream 属性比较 java stream 用法_java_04

无状态:指元素的处理不受之前元素的影响;
有状态:指该操作只有拿到所有元素之后才能继续下去。
非短路操作:指必须处理所有元素才能得到最终结果;
短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。
创建的Employee集合对象如下:

// 2. 中间操作
    List<Employee> emps = Arrays.asList(
            new Employee(102, "李四", 59, 6666.66), 
            new Employee(101, "张三", 18, 9999.99),
            new Employee(103, "王五", 28, 3333.33), 
            new Employee(104, "赵六", 8, 7777.77),
            new Employee(104, "赵六", 8, 7777.77), 
            new Employee(104, "赵六", 8, 7777.77),
            new Employee(105, "田七", 38, 5555.55)
            );

筛选与切片
filter:过滤流中的某些元素
limit(n):获取n个元素
skip(n):跳过n元素,配合limit(n)可实现分页
distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素

//筛选与切片
        List<Employee> list = EmployeeData.emps;
        //filter(Predicate p)--接收lambda,从流中排除某些元素
        Stream<Employee> stream = list.stream();
        //查询薪资大于7000
        stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
        System.out.println();
        //limit(n)截断流,使其元素不超过给定数量
        list.stream().limit(3).forEach(System.out::println);
        System.out.println();
        //skip(n)跳过元素,返回一个扔掉了前n个元素的流,如果元素不足n个,则返回空
        list.stream().skip(3).forEach(System.out::println);
        System.out.println();
        //distinct()筛选,用过流所生成元素的hashCode()和equals()去除重复元素
        list.stream().distinct().forEach(System.out::println);

映射
map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

List<String> list = Arrays.asList("a,b,c", "1,2,3");

        //将每个元素转成一个新的且不带逗号的元素
        Stream<String> s1 = list.stream().map(s -> s.replaceAll(",", ""));
        s1.forEach(System.out::println); // abc  123

        Stream<String> s3 = list.stream().flatMap(s -> {
            //将每个元素转换成一个stream
            String[] split = s.split(",");
            Stream<String> s2 = Arrays.stream(split);
            return s2;
        });
        s3.forEach(System.out::println); // a b c 1 2 3

排序

//sorted()-自然排序
        List<Integer> list3 = Arrays.asList(12, 31, 42, 123, 13, 51);
        list3.stream().sorted().forEach(System.out::println);

        //sorted(Comparator com)-定制排序
        list3.stream().sorted((i1,i2)->-(i1-i2)).forEach(System.out::println);
        list.stream()
                .sorted((x, y) -> {
                    if(x.getAge() == y.getAge()){
                        return x.getName().compareTo(y.getName());
                    }else{
                        return Integer.compare(x.getAge(), y.getAge());
                    }
                }).forEach(System.out::println);

3.3 终止操作

匹配,查找
allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
findFirst:返回流中第一个元素
findAny:返回流中的任意元素
count:返回流中元素的总个数
max:返回流中元素最大值
min:返回流中元素最小值

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

        boolean allMatch = list.stream().allMatch(e -> e > 10); //false
        boolean noneMatch = list.stream().noneMatch(e -> e > 10); //true
        boolean anyMatch = list.stream().anyMatch(e -> e > 4);  //true

        Integer findFirst = list.stream().findFirst().get(); //1
        Integer findAny = list.stream().findAny().get(); //1

        long count = list.stream().count(); //5
        Integer max = list.stream().max(Integer::compareTo).get(); //5
        Integer min = list.stream().min(Integer::compareTo).get(); //1

规约
T reduce(T identity, BinaryOperator accumulator) :可以将流中的元素反复结合起来,得到一个值。返回T。
Optional reduce(BinaryOperator accumulator) :可以将流中的元素反复结合起来,得到一个值。返回Optional。

//计算1-10的自然数的和
       List<Integer> 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);
       //计算公司员工总工资
       Stream<Double> doubleStream = emps.stream().map(Employee::getSalary);
       Optional<Double> sumMoney = doubleStream.reduce(Double::sum);
       System.out.println(sumMoney);

什么是Optional类

到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针髯常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java8类库的一部分。

Optional类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。
Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

收集
collect:接收一个Collector实例,将流中元素收集成另外一个数据结构。

//查找工资大于4000的员工,结果返回一个List或者Set
        List<Employee> employeeList = emps.stream().filter(e -> e.getSalary() > 4000).collect(Collectors.toList());
        employeeList.forEach(System.out::println);
        Set<Employee> employeeSet = emps.stream().filter(e -> e.getSalary() > 4000).collect(Collectors.toSet());
        employeeSet.forEach(System.out::println);