一、简介

JDK8中一个很重要的新特性就是Stream流,可以在写代码的时候节省很多的步骤,短短一行就可以完成对集合、数组等数据的筛选过滤和转换。

  • Stream是元素的集合,这点让Stream看起来用些类似Iterator;
  • 可以支持顺序和并行的对原Stream进行汇聚的操作;

可以把Stream当成一个高级版本的Iterator。原始版本的Iterator,用户只能一个一个的遍历元素并对其执行某些操作;高级版本的Stream,用户只要给出需要对其包含的元素执行什么操作,比如“过滤掉长度大于10的字符串”、“获取每个字符串的首字母”等,具体这些操作如何应用到每个元素上,交给Stream就好了。

二、使用Stream流

2.1 步骤

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

2.2 创建Stream

  1. /**
  2. * 1.创建Stream
  3. */
  4. public static void test1() {
  5. // 1.可以通过Collection系列集合提供的stream() 或 parallelStream() 获取流
  6. List<String> list = new ArrayList<>();
  7. Stream<String> stream1 = list.stream();
  8. // 2.通过 Arrays 中的静态方法 stream() 获取数组流
  9. Person[] person = new Person[10];
  10. Stream<Person> stream2 = Arrays.stream(person);
  11. // 3.通过 Stream 类中的静态方法 of()
  12. Stream<String> stream3 = Stream.of("aa", "bb", "cc");
  13. // 4.创建无限流
  14. // 迭代
  15. Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
  16. // 生成
  17. Stream<Double> stream5 = Stream.generate(Math::random);
  18. }

2.3 中间操作

注意:多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”
用于举例的集合:

  1. public static List<Person> personList = Arrays.asList(
  2. new Person(1, "AA"),
  3. new Person(2, "BB"),
  4. new Person(3, "CC"),
  5. new Person(4, "EE"),
  6. new Person(4, "DD"),
  7. new Person(4, "DD")
  8. );

2.3.1 筛选与切片

  1. filter —— 接收Lambda ,从流中排除某些元素。

     // 内部迭代:迭代操作由Stream API 完成
     public static void test2(){
         Stream<Person> personStream = personList.stream().filter(e -> e.getId() > 3);
         // 终止操作:一次性处理
         personStream.forEach(System.out::println);
     }
    
  2. limit —— 截断流,使其元素不超过给定数量。

     public static void test3(){
         Stream<Person> personStream = personList.stream().limit(2);
         personStream.forEach(System.out::println);
     }
    
  3. skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与limit(n)互补

     public static void test4(){
         Stream<Person> personStream = personList.stream().skip(2);
         personStream.forEach(System.out::println);
     }
    
  4. distinct —— 筛选,通过流所生成元素的hashCode()和equals()去除重复元素

     public static void test5(){
         Stream<Person> personStream = personList.stream().distinct();
         personStream.forEach(System.out::println);
     }
    

    2.3.2 映射

  5. 映射map—接收Lambda ,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

     public static void test6(){
         List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
         // 调用String的方法转为大写
         list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
         // 调用Person的getName方法获取名字
         personList.stream().map(Person::getName).forEach(System.out::println);
     }
    
  6. flatMap—接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

先写一个方法,该方法将传入的String解析为字符流

    public static Stream<Character> filterCharacter(String str) {
        List<Character> list = new ArrayList<>();
        for (Character ch : str.toCharArray()) {
            list.add(ch);
        }
        return list.stream();
    }
    public static void test7() {
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
        // 如果这里使用map,则获取的是Stream中还有多个Stream,类似于{{a,a,a},{b,b,b}}
        Stream<Stream<Character>> stream = list.stream().map(TestStream01::filterCharacter);
        // 遍历则需要两层
        stream.forEach(e->{
            e.forEach(System.out::println);
        });
        // 使用flatMap,则会直接将多个流连接成一个流,类似于{a,a,a,b,b,b}
        Stream<Character> characterStream = list.stream().flatMap(TestStream01::filterCharacter);
        // 遍历只需要一层
        characterStream.forEach(System.out::println);
    }

2.3.3 排序

  1. sorted()——自然排序,按照对象的Comparable排序

     public static void test8() {
         List<String> list = Arrays.asList("ccc", "bbb", "aaa", "eee", "ddd");
         list.stream().sorted().forEach(System.out::println);
     }
    
  2. sorted(Comparator com)——定制排序,按照传入的Comparator排序

    public static void test9() {
        personList.stream().sorted((e1, e2) -> {
            if (e1.getId().equals(e2.getId())) {
                return e1.getName().compareTo(e2.getName());
            } else {
                return e1.getId().compareTo(e1.getId());
            }
        }).forEach(System.out::println);
    }

2.4 终止操作

用于终止操作举例的集合:

    public static List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.99, Status.FREE),
            new Employee("李四", 58, 5555.55, Status.BUSY),
            new Employee("王五", 26, 3333.33, Status.VOCATION),
            new Employee("赵六", 36, 6666.66, Status.FREE),
            new Employee("田七", 12, 8888.88, Status.BUSY)
    );

2.4.1 查找与匹配

  1. allMatch——检查是否匹配所有元素
  2. anyMatch——检查是否至少匹配一个元素
  3. noneMatch——检查是否没有匹配所有元素

     public static void test10() {
         boolean b1 = employees.stream().allMatch((e) -> e.getStatus().equals(Status.BUSY));
         System.out.println(b1);
         boolean b2 = employees.stream().anyMatch((e) -> e.getStatus().equals(Status.BUSY));
         System.out.println(b2);
         boolean b3 = employees.stream().noneMatch((e) -> e.getStatus().equals(Status.BUSY));
         System.out.println(b3);
     }
    

    4.findFirst——返回第一个元素

     // 此处先以salary进行了排序,然后取出了第一个元素
     // 取出的元素被封装在Optional中,用于避免空值,可以通过orElse来设置为空时的值
     public static void test11() {
         Optional<Employee> op = employees.stream().sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).findFirst();
         System.out.println(op.get());
     }
    
  4. findAny—返回当前流中的任意元素

     // 先过滤出FREE状态元素,再从中取除符合要求的一个数据(串行流的话,肯定取出第一个符合的元素)
     public static void test12() {
         Optional<Employee> any = employees.stream().filter(e -> e.getStatus().equals(Status.FREE)).findAny();
         System.out.println(any.get());
     }
    
  5. count——返回流中元素的总个数

  6. max——返回流中最大值
  7. min——返回流中最小值

     public static void test13() {
         long count = employees.stream().count();
         System.out.println(count);
    
         Optional<Employee> max = employees.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
         System.out.println(max.get());
    
         Optional<Employee> min = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
         System.out.println(min.get());
     }
    

    2.4.2 归约

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

     public static void test14() {
         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);
         System.out.println("=================================");
         // 获取employees工资总和
         Optional<Double> salary = employees.stream().map(Employee::getSalary).reduce(Double::sum);
         System.out.println(salary.get());
     }
    

    2.4.3 收集

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

    public static void test15() {
         // 收集到List中
         List<String> nameList = employees.stream().map(Employee::getName).collect(Collectors.toList());
         nameList.forEach(System.out::println);
         System.out.println("===============");
         // 收集到Set中,去重
         Set<String> nameSet = employees.stream().map(Employee::getName).collect(Collectors.toSet());
         nameSet.forEach(System.out::println);
         System.out.println("===============");
         // 收集到特殊的Set中,例如HashSet
         HashSet<String> nameHashSet = employees.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
         nameHashSet.forEach(System.out::println);
         System.out.println("===============");
         // 总数
         Long count = employees.stream().collect(Collectors.counting());
         System.out.println(count);
         System.out.println("===============");
         // 平均值
         Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
         System.out.println(avg);
         System.out.println("===============");
         // 总和
         Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
         System.out.println(sum);
         System.out.println("===============");
         // 分组
         Map<Status, List<Employee>> map1 = employees.stream().collect(Collectors.groupingBy(Employee::getStatus));
         System.out.println(map1);
         System.out.println("===============");
         // 多级分组
         Map<Status, Map<String, List<Employee>>> map2 = employees.stream().collect(Collectors.groupingBy(Employee::getStatus,
                 Collectors.groupingBy(e -> {
                     if (e.getAge() <= 35) {
                         return "青年";
                     } else if (e.getAge() <= 50) {
                         return "中年";
                     } else {
                         return "老年";
                     }
                 })));
         System.out.println(map2);
         System.out.println("===============");
         // 分区 满足条件一个区,不满足一个区
         Map<Boolean, List<Employee>> map3 = employees.stream().collect(Collectors.partitioningBy(e -> e.getSalary() > 8000));
         System.out.println(map3);
         System.out.println("===============");
         // 获取DoubleSummaryStatistics,再从中可获取最小值,最大值,平均值,总数,总和
         DoubleSummaryStatistics dss = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
         System.out.println(dss.getMax());
         System.out.println(dss.getMin());
         System.out.println(dss.getAverage());
         System.out.println(dss.getCount());
         System.out.println(dss.getSum());
         // 连接字符串
         String nameStr = employees.stream().map(Employee::getName).collect(Collectors.joining(",","===","==="));
         System.out.println(nameStr);
     }