java 8之stream流

Java 8中添加了一个新的功能称为Stream流,可以通过使用一种声明的方式处理数据。Stream 流使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

Stream这种风格将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

1. 什么是流

如果把集合作为流的数据源,创建流时不会导致数据流动; 如果流的终止操作需要值时,流就会从集合中获取值; 流只使用一次。流的中心思想就是延迟计算,流直到需要时才计算值。
java 8之stream流_第1张图片
Stream可以由数组或集合创建,对流的操作分为两种:

  • 中间操作,每次返回一个新的流,可以有多个。
  • 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。

2. Stream总览

直接上思维导图,清晰明了。
java 8之stream流_第2张图片
xmind文件下载

3.Stream详解

3.1 Stream的分类

可以创建普通的Stream流和特定的Stream流。

        /**
         * stream的分类
         */

        // 普通流
        List<String> list1 = Arrays.asList("1", "2", "3");
        Stream<String> stream1 = list1.stream();

        List<Integer> list2 = Arrays.asList(1, 2, 3);
        Stream<Integer> stream2 = list2.stream();

        // 特化流
        IntStream stream3 = IntStream.of(1, 2, 3);
        stream3.forEach(System.out::println);

也可以创建stream和 parallelStream,stream是顺序流,由主线程按顺序对流执行操作,而 parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。并且只有当数据量比较大时,我们使用并行流才具备优势。

        // 串行
        Stream<String> stream4 = Stream.of("1", "2", "3");

        // 并行
        // 1.
       Stream<String> parallelStream1 =  list1.parallelStream();
       parallelStream1.forEach(System.out::print);

       // 2.
        Stream<String> parallelStream2 = list1.stream().parallel();

两者的主要区别:
java 8之stream流_第3张图片

3.2 Stream的创建

通过使用 java.util.Collection.stream() 方法用集合创建流,使用 java.util.Arrays.stream(T[]array)方法用数组创建流。

List<String> list = Arrays.asList("1", "2", "3");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();

int[] array={1, 2, 3};
IntStream stream = Arrays.stream(array);

通过使用 Stream的静态方法:of()、iterate()、generate()

        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);

        Stream<Integer> stream2 = Stream.iterate((int)Math.random(), (x) -> x + 1).limit(4);
        stream2.forEach(System.out::println);

        Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
        stream3.forEach(System.out::println);

通过使用Stream的builder()方法创建构建器,通过构建器创建stream流。

        Stream.Builder<String> builder = Stream.builder();
        builder.add("1");
        builder.add("2");
        builder.add("3");
        Stream<String> stream4 = builder.build();
        stream4.forEach(System.out::print);

3.3 Stream的使用

使用stream流之前,我们先来理解一个概念:Optional类。

3.3.1 Optional类

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。因此,Optional 类的引入很好的解决空指针异常。

ofNullable方法

        //为指定的值创建Optional对象,不管所传入的值为null不为null,创建的时候都不会报错
        Optional<String> Optional1 = Optional.ofNullable(null);
        Optional<String> Optional2 = Optional.ofNullable("picacho");

ifPresent方法

        String str = "picacho";
        Optional<String> optional = Optional.ofNullable(str);
        // 如果值不是为空的,那么就可以直接在这个里面来进行处理
        optional.ifPresent(s -> System.out.println(str+"hello"));

orElse方法

        Optional<Object> empty = Optional.empty();
        System.out.println(empty.orElse("hello"));
3.3.2 遍历/匹配(foreach/find/match)
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
 
        // 遍历输出符合条件的元素
        list.stream().filter(x -> x > 3).forEach(System.out::println);
        // 匹配第一个
        Optional<Integer> findFirst = list.stream().filter(x -> x > 3).findFirst();
        // 匹配任意(适用于并行流)
        Optional<Integer> findAny = list.parallelStream().filter(x -> x > 3).findAny();
        // 是否包含符合特定条件的元素
        boolean anyMatch = list.stream().anyMatch(x -> x < 2);
        System.out.println("匹配第一个值:" + findFirst.get());
        System.out.println("匹配任意一个值:" + findAny.get());
        System.out.println("是否存在大于6的值:" + anyMatch);
3.3.3 过滤(filter)
        List<Person> personList = new ArrayList<Person>();

        personList.add(new Person("zhangsan", 8, 3000));
        personList.add(new Person("lisi", 18, 5000));
        personList.add(new Person("wangwu", 28, 7000));
        personList.add(new Person("sunliu", 38, 9000));

        // 筛选出年龄大于18
       List<Person> personList1 =  personList.stream().filter(x -> x.getAge() > 18).collect(Collectors.toList());
       personList1.forEach(System.out::println);
3.3.4 聚合(max/min/count)
        List<String> list = Arrays.asList("123", "1234", "12345", "12");
        Comparator<? super String> comparator = Comparator.comparing(String::length);
        Optional<String> max = list.stream().max(comparator);
        System.out.println(max.get());
        Optional<String> min = list.stream().min(comparator);
        System.out.println(min.get());
        long count = list.stream().filter(x -> x.length() > 3).count();
        System.out.println(count);
3.3.5 映射(map/flatMap)

map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

        // 将姓名大写
        List<String> list1 = personList.stream().map(person -> person.getName().toUpperCase())
                                        .collect(Collectors.toList());
        System.out.println(list1);


        // 工资涨1000
        List<Person> list2 = personList.stream().map(person -> {
            person.setAccount(person.getAccount() + 1000);
            return person;
        }).collect(Collectors.toList());
        System.out.println(list2);
        // 将两个字符数组合并称一个新的字符数组
        String[] arr = {"z, h, a, n, g", "s, a, n"};
        List<String> collect = Arrays.stream(arr).flatMap(x -> {
            String[] array = x.split(",");
            Stream<String> streamTmp = Arrays.stream(array);
            return streamTmp;
        }).collect(Collectors.toList());
        System.out.println(collect);
3.3.6 collect

Collectors提供了一系列用于数据统计的静态方法:

计数: count

平均值: averagingInt、 averagingLong、 averagingDouble

最值: maxBy、 minBy

求和: summingInt、 summingLong、 summingDouble

        //统计员工人数
        Long count1 = personList.stream().collect(Collectors.counting());
        //求平均工资
        Double average = personList.stream().collect(Collectors.averagingDouble(Person::getAccount));
        //求最高工资
        Optional<Integer> max1 = personList.stream().map(Person::getAccount).collect(Collectors.maxBy(Integer::compare));
        //求工资之和
        Integer sum = personList.stream().collect(Collectors.summingInt(Person::getAccount));
        //一次性统计所有信息
        DoubleSummaryStatistics collect1 = personList.stream().collect(Collectors.summarizingDouble(Person::getAccount));
        System.out.println("统计员工人数:"+count);
        System.out.println("求平均工资:"+average);
        System.out.println("求最高工资:"+max);
        System.out.println("求工资之和:"+sum);
        System.out.println("一次性统计所有信息:"+collect1);
3.3.7 收集(toList/toSet/toMap)
  // 取出大于18岁的员工转为map
  Map<String, Person> collect3 = personList.stream().filter(x -> x.getAge() > 18).collect(Collectors.toMap(Person::getName, y -> y));
    System.out.println(collect3);
3.3.8 规约(reduce)

归约,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。

        List<Integer> list3 = Arrays.asList(1, 2, 3, 4);
        //求和
        Optional<Integer> reduce = list3.stream().reduce((x,y) -> x + y);
        System.out.println("求和:"+reduce.get());

        //求最大值
        Optional<Integer> reduce3 = list3.stream().reduce((x,y) -> x > y ? x : y);
        System.out.println("求最大值:"+reduce3.get());
3.3.9 连接(joining())

joining可以将stream中的元素用特定的连接符(默认直接连接)连接成一个字符串。

String names = personList.stream().map(person -> person.getName()).collect(Collectors.joining("-"));
        System.out.println(names);
3.3.10 分组(partitioningBy/groupingBy)

分区:将stream按条件分为两个 Map。

分组:将集合分为多个Map。

        List<Person> personList3 = new ArrayList<Person>();

        personList3.add(new Person("zhangsan", 18, 3000));
        personList3.add(new Person("lisi", 18, 5000));
        personList3.add(new Person("wangwu", 28, 7000));
        personList3.add(new Person("sunliu", 38, 9000));
        // 将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> part = personList3.stream().collect(Collectors.partitioningBy(x -> x.getAccount() > 8000));

        // 将员工先按年龄分组,再按工资分组
        Map<Integer, Map<String, List<Person>>> group = personList3.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.groupingBy(Person::getName)));
        System.out.println("员工按薪资是否大于8000分组情况:" + part);

        System.out.println("员工按性别、地区:" + group);

3.3.11 排序(sorted)
       // 按工资升序排序(自然排序)
        List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getAccount)).map(Person::getName)
                .collect(Collectors.toList());
        // 按工资倒序排序
        List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getAccount).reversed())
                .map(Person::getName).collect(Collectors.toList());
        // 先按工资再按年龄升序排序
        List<String> newList3 = personList.stream()
                .sorted(Comparator.comparing(Person::getAccount).thenComparing(Person::getAge)).map(Person::getName)
                .collect(Collectors.toList());
        // 先按工资再按年龄自定义排序(降序)
        List<String> newList4 = personList.stream().sorted((p1, p2) -> {
            if (p1.getAccount() == p2.getAccount()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getAccount() - p1.getAccount();
            }
        }).map(Person::getName).collect(Collectors.toList());

        System.out.println("按工资升序排序:" + newList);
        System.out.println("按工资降序排序:" + newList2);
        System.out.println("先按工资再按年龄升序排序:" + newList3);
        System.out.println("先按工资再按年龄自定义降序排序:" + newList4);
3.3.12 提取/组合(concat()\distinct()\limit()\skip())
        String[] arr1 = { "a", "b", "c", "d" };
        String[] arr2 = { "d", "e", "f", "g" };
        Stream<String> stream11 = Stream.of(arr1);
        Stream<String> stream12 = Stream.of(arr2);
        // concat:合并两个流 distinct:去重
        List<String> newList11 = Stream.concat(stream11, stream12).distinct().collect(Collectors.toList());
        // limit:限制从流中获得前n个数据
        List<Integer> collect11 = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        // skip:跳过前n个数据
        List<Integer> collect12 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());

        System.out.println("流合并:" + newList11);
        System.out.println("limit:" + collect11);
        System.out.println("skip:" + collect12);

Sream流还有很多更高级的用法,需要多看API,多练习来掌握该类的使用。在项目中使用该类可以使得代码更加精炼和美观。

你可能感兴趣的:(java,se,java)