Java之Stream流快速入门

375 阅读8分钟

快速入门的小栗子,遍历列表

List<Integer> list = Arrays.asList(1,2,3,4,5);
list.stream().forEach(System.out::println);//即可打印出1,2,3,4,5

Stream流是什么

  • 我们可以把集合,数组想象成存储数据的容器,相当于存储原材料的仓库,而Stream则是对这些原材料加工的流水线,是对数据进行的加工。入门的例子中,list存储了数据,而Stream操作对数据进行了加工,即打印。
  • 流不存储元素,流只是数据的加工者。
  • 流不会改变源对象,返回的依然是Stream
  • 流的操作是延迟执行的,有中止操作时才会执行

为什么用Stream流

集合类库主要依赖于外部迭代(external iteration)。Collection 实现 Iterable 接口,从而使得用户可以依次遍历集合的元素。流相比于for循环是内部迭代的,用户把对迭代的控制权交给类库,并向类库传递迭代时所需执行的代码,从而允许类库进行各种各样的优化(例如乱序执行、惰性求值和并行等等)。总的来说,内部迭代使得外部迭代中不可能实现的优化成为可能。

使用Stream流的步骤

  • 和把大象装进冰箱的步骤一样多,都需要三步:
    • 创建流:通过一个数据源,如集合,数组,来创建一个流
    • 中间操作流: 中间操作链,对数据源的数据进行处理
    • 结束流:一旦执行中止操作,就执行中间操作链,并产生结果。之后不会再被使用。

常用的API

  • 创建流的API
    • 通过集合创建流
      • default Stream stream():返回一个顺序流
      • default Stream parallelStream():返回一个并行流
      List<Integer> list = Arrays.asList(1,2,3);
      Stream<Integer> stream1= list.stream();
      Stream<Integer> stream2= list.parallelStream();
      
    • 通过数组创建流,Arrays的静态方法stream()可以获取数组流
      • static Stream stream(T[] array):返回一个流
      • public static IntStream stream(int[] array)
      • public static LongStream stream(long[] array)
      • public static DoubleStream stream(double[] array)
      //调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流
          int[] arr = new int[]{1,2,3,4,5,6};
          IntStream stream = Arrays.stream(arr);
          double[] d = new double[]{1.0,2.0};
          DoubleStream doubleStream = Arrays.stream(d);
          
          Employee e1 = new Employee(1001,"Tom");
          Employee e2 = new Employee(1002,"Jerry");
          Employee[] arr1 = new Employee[]{e1,e2};
          Stream<Employee> stream1 = Arrays.stream(arr1);
      
    • 通过Stream类的静态方法of()
      • public static Stream of(T... values): 返回一个流
      Stream<Integer> stream = Stream.of(1,2,3,4,5);
      
    • 创建无限流,使用静态方法Stream.iterate()和Stream.generate(),创建无限流。
      • 迭代 public static Stream iterate(final T seed,final UnaryOperator f)
      • 生成 public static Stream generate(Supplier s)
      //  迭代
      //  public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
          //遍历前10个偶数
          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);
      
  • 中间操作流的API 多个中间操作可以连接起来形成一个流水线,除非流水线上触发中止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为“惰性求值”
  • 过滤filter(Predicate p) 删除不想要的元素
  • 截断limit(long maxSize) 获取指定数量
  • 去重distinct() 通过hashCode()和equals()去重
  • 跳过元素skip(long n) ,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流
        int[] arr={1,2,2,5,4,3};
        //filter(Predicate p) 过滤元素
        Arrays.stream(arr).filter(e->e>2).forEach(System.out::println);//[5,4,3]
        //limit
        Arrays.stream(arr).limit(3).forEach(System.out::println);//[1,2,2]
        //distinct
        Arrays.stream(arr).distinct().forEach(System.out::println);//[1,2,5,4,3]
        //skip
        Arrays.stream(arr).skip(2).forEach(System.out::println);//[2,5,4,3]
  • 映射

    • map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
    • mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 DoubleStream
    • mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 LongStream
    • flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另 一个流,然后把所有流连接成一个流
    Stream<String> stringStream = Stream.of("abc");
    stringStream.map(e->e.toUpperCase()).forEach(System.out::println);//[ABC]
    Stream.of("abc").map(s->s.split("")).forEach(System.out::println);//[Ljava.lang.String;@ffaaaf0
    Stream.of("abc").map(s->s.split("")).flatMap(Arrays::stream).forEach(System.out::println);//[a,b,c]
    
  • 排序

    • sorter() 产生一个新流,其中按自然顺序排序
    • sorted(Comparator com) 产生一个新流,其中按比较器顺序排序
          Stream.of(2,1,3,5,4).sorted().forEach(System.out::println);//1,2,3,4,5
          Stream.of(2,1,3,5,4).sorted((o1,o2)->o2.compareTo(o1)).forEach(System.out::println);//5,4,3,2,1
    
  • 结束流的API(流进行了终止操作后不能再次使用)

  • 匹配与查找

方法 描述 例子
allMatch(Predicate p) 检查是否匹配所有元素 boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18)
anyMatch(Predicate p) 检查是否只匹配一个元素 employees.stream().anyMatch(e -> e.getSalary() > 10000)
noneMatch(Predicate p) 检查是否没有匹配所有元素 employees.stream().noneMatch(e -> e.getName().startsWith("雷"))
findFirst() 返回第一个元素 Optional<Employee> employee = employees.stream().findFirst()
findAny() 返回当前流中的任意元素 Optional<Employee> employee1 = employees.parallelStream().findAny()
count() 返回流中元素总数 employees.stream().filter(e -> e.getSalary() > 5000).count()
max(Comparator c) 返回流中最大值 Optional<Double> maxSalary = salaryStream.max(Double::compare)
min(Comparator c) 返回流中最小值 Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
forEach(Consumer c) 内部迭代 employees.stream().forEach(System.out::println)
List<Employee> 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> employee = employees.stream().findFirst();
        System.out.println(employee);
//        findAny——返回当前流中的任意元素
        Optional<Employee> employee1 = employees.parallelStream().findAny();
        System.out.println(employee1);
        
     List<Employee> employees = EmployeeData.getEmployees();
        // count——返回流中元素的总个数
        long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
        System.out.println(count);
//        max(Comparator c)——返回流中最大值
//        练习:返回最高的工资:
        Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
        Optional<Double> maxSalary = salaryStream.max(Double::compare);
        System.out.println(maxSalary);
//        min(Comparator c)——返回流中最小值
//        练习:返回最低工资的员工
        Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(employee);
        System.out.println();
//        forEach(Consumer c)——内部迭代
        employees.stream().forEach(System.out::println);

        //使用集合的遍历操作
        employees.forEach(System.out::println);   
  • 归约
方法 描述 例子
reduce(T iden,BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回T list.stream().reduce(0, Integer::sum)
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值,返回Optional<T> Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2);
//        reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T
//        练习1:计算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);


//        reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
//        练习2:计算公司所有员工工资的总和
        List<Employee> employees = EmployeeData.getEmployees();
        Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
//        Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
        Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2);
        System.out.println(sumMoney.get());
  • 收集
方法 描述
collect(Collector c) 将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

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

方法 返回类型 作用 例子
toList List<T> 把流中元素收集到List List<Employee> emps = list.stream().collect(Collectors.toList())
toSet Set<T> 把流中元素收集到Set Set<Employee> emps = list.stream().collect(Collectors.toSet())
toCollection Collection<T> 把流中元素收集到创建的集合 Collection<Employee> emps = list.stream().collect(Collectors.toCollection(ArrayList::new))
counting Long 计算流中元素个数 long count = list.stream().collect(Collectors.counting())
summingInt Integer 对流中元素的整数属性求和 int total = list.stream().collect(Collectors.summingInt(Employee::getSalary))
averageingInt Double 计算流中元素Integer属性的平均值 double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary))
summarizingInt IntSummaryStatistics 收集流中Integer属性的统计值。如平均值 int SummaryStatistics=list.stream().collect(Collectors.summarizingInt(Employee::getSalary))
joining String 连接流中每个字符串 String str= list.stream().map(Employee::getName).collect(Collectors.joining())
maxBy Optional<T> 根据比较器选择最大值 Optionalmax=list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)))
minBy Optional<T> 根据比较器选择最小值 Optional min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)))
reducing 归约产生的类型 从一个作为累加器的初始值开始, 利用BinaryOperator与流中元素逐 个结合,从而归约成单个值 int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum))
collectingAndThen 转换函数返回的类型 包裹另一个收集器,对其结果转换函数 int how=list.stream().collect(Collectors.collectingAndThen(Collectors.toList(),List::size))
groupingBy Map<K,List<T>> 根据某属性值对流分组,属性为K,结果为V Map<Emp.Status,List<Emp>> map=list.stream().collect(Collectors.groupingBy(Employee::getStatus))
partitioningBy Map<Boolean,List<T>> 根据true或false进行分区 Map<Boolean,List<Emp>> vd=list.stream().collect(Collectors.partitioningBy(Employee::getManage))
//        collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
//        练习1:查找工资大于6000的员工,结果返回为一个List或Set

        List<Employee> employees = EmployeeData.getEmployees();
        List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());

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

        employeeSet.forEach(System.out::println);