您的位置:  首页 > 技术 > java语言 > 正文

让Java8的Stream更简单

2021-11-01 13:00 https://my.oschina.net/u/5079097/blog/5291463 PHP开发工程师 次阅读 条评论

1. 为什么有经验的老手更倾向于使用Stream

  • 性能优势,(大数据量)相较于迭代器,速度更快
  • 支持串行与并行处理,并行处理更能充分利用CPU的资源
  • Stream 是一种计算数据的流,它本身不会存储数据
  • 支持函数式编程
  • 代码优雅,让代码更高效,干净,简洁

2. Stream 的使用方式

三步操作:

  • 创建Stream
  • 中间操作
  • 终止操作

3. Stream 的创建

Stream 的 创建都会依赖于数据源,通常是容器或者数组 Stream 流的创建大致分为4中,最为常用的就是通过集合创建

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import java.util.stream.IntStream;

import java.util.stream.Stream;



public class CreateStreamDemo {



    public static void main(String[] args) {

        // 1 通过集合创建Stream也是用的最多的一种形式

        List<String> strList = new ArrayList<>();

        strList.add("a");

        strList.add("b");

        strList.add("c");

        // 创建串行操作流

        Stream<String> stream = strList.stream();

        // 创建并行操作流

        Stream<String> parallelStream = strList.parallelStream();

        // 2 通过数组创建Stream

        int[] arr = new int[]{1,2,3};

        IntStream intStream = Arrays.stream(arr);

        // 3 通过Stream.of

        Stream<Integer> integerStream = Stream.of(1,2,3);

        Stream<String> stringStream = Stream.of("a","b","c");

        // 4 无限流

        // 每隔五个数取一个

        Stream.iterate(0, t -> t + 5).forEach(System.out::println); // 迭代

        Stream.generate(Math::random).forEach(System.out::println); // 生成

    }

}



复制代码

4. Stream 中间操作

Stream 中间操作,我们最为常用的就是过滤,去重,排序 本章包含我们开发最常用的对对象的去重,和更据对象中的对个属性组合排序

import com.zhj.java8.bean.Student;



import java.util.ArrayList;

import java.util.Comparator;

import java.util.List;

import java.util.TreeSet;

import java.util.stream.Stream;



import static java.util.stream.Collectors.collectingAndThen;

import static java.util.stream.Collectors.toCollection;





public class MiddleStreamDemo {



    public static void main(String[] args) {



        List<Student> students = new ArrayList<>();

        students.add(new Student(1,"小华",23,1));

        students.add(new Student(1,"小华",23,2));

        students.add(new Student(2,"小米",20,2));

        students.add(new Student(3,"小果",30,3));

        students.add(new Student(4,"小维",18,2));



        // 过滤

        students.stream().filter(stu -> stu.getAge() > 20).forEach(System.out::println);



        // 去重

        // 对对象去重是根据引用去重,内容重复并不会去重,除非重写equals和hashCode方法

        System.out.println("----------去重----------");

        System.out.println("去重1----------");

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

        // 对集合中对象某些属性去重,不重写equals和hashCode方法,只能借助其他数据结构来辅助去重

        // 单个属性可以stu -> stu.getId()

        // 多个属性可以stu -> stu.getId() + ";" + stu.getName()

        System.out.println("去重2----------");

        ArrayList<Student> distinctList = students.stream().collect(

                collectingAndThen(toCollection(() -> new TreeSet<>(Comparator.comparing(stu -> stu.getId() + ";" + stu.getName()))), ArrayList::new)

        );

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



        // 排序 支持定义排序方式

        // sorted 默认使用 自然序排序, 其中的元素必须实现Comparable 接口

        System.out.println("----------排序----------");

        System.out.println("排序1----------");

        students.stream().sorted().forEach(System.out::println);

        // sorted(Comparator<? super T> comparator) :我们可以使用lambada 来创建一个Comparator 实例。可以按照升序或着降序来排序元素。

        System.out.println("排序2----------");

        students.stream()

                .sorted(Comparator.comparing(Student::getAge,Comparator.reverseOrder())) // ,Comparator.reverseOrder() 逆序

                .forEach(System.out::println);

        // 创建比较器,通过对比较器内容的定义实现对多个属性进行排序,类似sql中连续的orderBy

        System.out.println("排序3----------");

        students.stream().sorted(

                (s1,s2) -> {

                    if (s1.getAge() == s2.getAge()) {

                        return s1.getSex().compareTo(s2.getSex());

                    } else {

                        return -s1.getAge().compareTo(s2.getAge());

                    }

                }

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

        System.out.println("排序4----------");

        Comparator<Student> studentComparator = (s1,s2) -> {

            Integer age1 = s1.getAge();

            Integer age2 = s2.getAge();

            if (age1 != age2) return age1 - age2;

            Integer sex1 = s1.getSex();

            Integer sex2 = s2.getSex();

            if (sex1 != sex2) return sex2 - sex1;

            return 0;

        };

        students.stream().sorted(studentComparator).forEach(System.out::println);



        // 截取 截取前三个元素

        System.out.println("----------截取----------");

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



        // 跳过 跳过前3个元素

        System.out.println("----------跳过----------");

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



        // 映射

        System.out.println("----------映射----------");

        System.out.println("映射Map----------");

        // map接收Lambda,将元素转换其他形式,或者是提取信息,并将其映射成一个新的元素

        Stream<Stream<Student>> streamStream1 = students.stream().map(str -> filterStudent(str));

        streamStream1.forEach(sm -> sm.forEach(System.out::println));

        System.out.println("映射flatMap----------");

        // map接收Lambda,将流中的每一个元素转换成另一个流,然后把所有流连成一个流 扁平化映射

        Stream<Student> studentStream2 = students.stream().flatMap(str -> filterStudent(str));

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

        

        // 消费

        System.out.println("----------消费----------");

        students.stream().peek(stu -> stu.setAge(100)).forEach(System.out::println);

    }



    public static Stream<Student> filterStudent(Student student) {

        student = new Student();

        return Stream.of(student);

    }

}



复制代码

Student

public class Student implements Comparable<Student> {



    private Integer id;

    private String name;

    private Integer age;

    private Integer sex;



    public Student() {

    }



    public Student(Integer id, String name, Integer age, Integer sex) {

        this.id = id;

        this.name = name;

        this.age = age;

        this.sex = sex;

    }



    public Integer getId() {

        return id;

    }



    public void setId(Integer id) {

        this.id = id;

    }



    public String getName() {

        return name;

    }



    public void setName(String name) {

        this.name = name;

    }



    public Integer getAge() {

        return age;

    }



    public void setAge(Integer age) {

        this.age = age;

    }



    public Integer getSex() {

        return sex;

    }



    public void setSex(Integer sex) {

        this.sex = sex;

    }



    @Override

    public String toString() {

        return "Student{" +

                "id='" + id + '\'' +

                ", name='" + name + '\'' +

                ", age='" + age + '\'' +

                ", sex=" + sex +

                '}';

    }



    @Override

    public int compareTo(Student o) {

        return this.getAge() - o.getAge();

    }

}

复制代码

5. Stream 终止操作

Stream 的终止操作,最常用的就是讲处理过的数据收集到新的容器中,同时可以实现向Sql聚合函数,分组的一些效果

package com.zhj.java8.stream;



import com.zhj.java8.bean.Student;



import java.util.*;

import java.util.stream.Collectors;



public class TerminationStreamDemo {



    public static void main(String[] args) {

        List<Student> students = new ArrayList<>();

        students.add(new Student(1,"小华",23,1));

        students.add(new Student(2,"小米",20,2));

        students.add(new Student(3,"小果",30,3));

        students.add(new Student(4,"小维",18,2));

        students.add(new Student(5,"小华",23,2));

        System.out.println("--------------------匹配聚合操作--------------------");

        // allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false

        boolean allMatch = students.stream().allMatch(stu -> stu.getAge() > 10);

        System.out.println("全部符合大于10岁条件:" + allMatch);

        // noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false

        boolean noneMatch = students.stream().noneMatch(stu -> stu.getAge() > 10);

        System.out.println("全部不符合大于10岁条件:" + noneMatch);

        // anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false

        boolean anyMatch = students.stream().anyMatch(stu -> stu.getAge() > 20);

        System.out.println("含有任意符合大于20岁条件:" + anyMatch);



        // findFirst:返回流中第一个元素

        Student findFirst = students.stream().findFirst().get();

        System.out.println("第一个学生:" + findFirst);

        // findAny:返回流中的任意元素

        Student findAny = students.stream().findAny().get();

        System.out.println("任意一个学生:" + findAny);



        //  count:返回流中元素的总个数

        long count = students.stream().count();

        System.out.println("学生总数:" + count);

        // max:返回流中元素最大值

        Student max = students.stream().max(Student::compareTo).get();

        System.out.println("年龄最大学生:" + max);

        // max:返回流中元素最大值

        Student min = students.stream().min(Student::compareTo).get();

        System.out.println("年龄最小学生:" + min);



        System.out.println("--------------------规约操作--------------------");

        System.out.println("学生年龄总和:" + students.stream().map(Student::getAge).reduce(Integer::sum));

        System.out.println("学生年龄最大:" + students.stream().map(Student::getAge).reduce(Integer::max));



        System.out.println("--------------------收集操作--------------------");

        List<Student> list = students.stream().collect(Collectors.toList());

        Set<Student> set = students.stream().collect(Collectors.toSet());

        Map<Integer, String> map = students.stream().collect(Collectors.toMap(Student::getId, Student::getName));

        String joinName = students.stream().map(Student::getName).collect(Collectors.joining(",", "(", ")"));

        // 总数

        students.stream().collect(Collectors.counting());

        // 最大年龄

        students.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get();

        // 年龄和

        students.stream().collect(Collectors.summingInt(Student::getAge));

        // 平均年龄

        students.stream().collect(Collectors.averagingDouble(Student::getAge));

        

        // 信息合集

        DoubleSummaryStatistics statistics = students.stream().collect(Collectors.summarizingDouble(Student::getAge));

        System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage());

        

        // 分组

        Map<Integer, List<Student>> collect = students.stream().collect(Collectors.groupingBy(Student::getSex));

        System.out.println(collect);

        //多重分组,先根据性别分再根据年龄分

        Map<Integer, Map<Integer, List<Student>>> typeAgeMap = list.stream().collect(Collectors.groupingBy(Student::getSex, Collectors.groupingBy(Student::getAge)));

        

        //分区

        //分成两部分,一部分大于20岁,一部分小于等于20岁

        Map<Boolean, List<Student>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 20));

        

        //规约

        Integer allAge = list.stream().map(Student::getAge).collect(Collectors.reducing(Integer::sum)).get();

        System.out.println(allAge);

    }

}

复制代码

6. Stream 特性

  • 中间操作惰性执行

    多个中间操作的话,不会多次循环,多个转换操作只会在终止操作的时候融合起来,一次循环完成

  • 内部迭代

  • 找到符合条件的数据后边的迭代不会进行

  • 流的末端操作只有一次

    异常:stream has already been operated upon or closed

    意思是流已经被关闭了,这是因为当我们使用末端操作之后,流就被关闭了,无法再次被调用,如果我们想重复调用,只能重新打开一个新的流。

最后

如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163相互学习,我们会有专业的技术答疑解惑

如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点star:http://github.crmeb.net/u/defu不胜感激 !

  • 0
    感动
  • 0
    路过
  • 0
    高兴
  • 0
    难过
  • 0
    搞笑
  • 0
    无聊
  • 0
    愤怒
  • 0
    同情
热度排行
友情链接