【java8】持续精进-之流式数据处理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014331288/article/details/76184850

流式处理简介

在我接触到java8流式处理的时候,我的第一感觉是流式处理让集合操作变得简洁了许多,通常我们需要多行代码才能完成的操作,借助于流式处理可以在一行中实现。比如我们希望对一个包含整数的集合中筛选出所有的偶数,并将其封装成为一个新的List返回,那么在java8之前,我们需要通过如下代码实现:

 /**
     * 准备数据
     *
     * @return
     */
    public static List<Integer> getData() {
        return Arrays.asList(1, 2, 4, 3, 5, 6, 7, 8, 9, 10, 11, 23);
    }
    public static void main(String[] args) {

        List<Integer> data = getData();
        List<Integer> evens = new ArrayList<>();
        for (Integer num : data) {
            if (num % 2 == 0) {
                evens.add(num);
            }
        }
    }

通过java8的流式处理,我们可以将代码简化为:

    /**
     * 准备数据
     *
     * @return
     */
    public static List<Integer> getData() {
        return Arrays.asList(1, 2, 4, 3, 5, 6, 7, 8, 9, 10, 11, 23);
    }
    public static void main(String[] args) {

        List<Integer> data = getData();

        List<Integer> integerList = data.stream()
                .filter(num -> num % 2 == 0)
                .collect(Collectors.toList());
    }

先简单解释一下上面这行语句的含义,stream操作将集合转换成一个流,filter执行我们自定义的筛选处理,这里是通过lambda表达式筛选出所有偶数,最后我们通过collect对结果进行封装处理,并通过Collectors.toList()指定其封装成为一个List集合返回。

一个流式处理可以分为三个部分:转换成流、中间操作、终端操作
流->中间操作….->中间操作->终端操作

实战

1.数据准备

@Data
public class Student {

    /** 学号 */
    private long id;

    /**xingming*/
    private String name;

    private int age;

    /** 年级 */
    private int grade;

    /** 专业 */
    private String major;

    /** 学校 */
    private String school;

    public Student(long id, String name, int age, int grade, String major, String school) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.grade = grade;
        this.major = major;
        this.school = school;
    }
}
 static List<Student> students = new ArrayList<Student>() {
        {
            add(new Student(20160001, "张三", 20, 1, "土木工程", "武汉大学"));
            add(new Student(20160002, "李四", 21, 2, "信息安全", "武汉大学"));
            add(new Student(20160003, "王五", 22, 3, "经济管理", "武汉大学"));
            add(new Student(20160004, "赵六", 21, 2, "信息安全", "武汉大学"));
            add(new Student(20161001, "张琦", 21, 2, "机械与自动化", "华中科技大学"));
            add(new Student(20161002, "阿德", 23, 4, "土木工程", "华中科技大学"));
            add(new Student(20161003, "阿哥", 23, 4, "计算机科学", "华中科技大学"));
            add(new Student(20162001, "阿房", 22, 3, "土木工程", "浙江大学"));
            add(new Student(20162002, "阿飞", 23, 4, "计算机科学", "浙江大学"));
            add(new Student(20163001, "阿德", 24, 5, "土木工程", "南京大学"));
        }
    };```

2.1 过滤操作(filter,distinct,limit,skip)

2.1.1 filter
  在前面的例子中我们已经演示了如何使用filter,其定义为:Stream<T> filter(Predicate<? super T> predicate),filter接受一个谓词Predicate,我们可以通过这个谓词定义筛选条件,在介绍lambda表达式时我们介绍过Predicate是一个函数式接口,其包含一个test(T t)方法,该方法返回boolean。现在我们希望从集合students中筛选出所有浙江大学的学生,那么我们可以通过filter来实现,并将筛选操作作为参数传递给filter
    List<Student> collect = students.stream()
            .filter(student -> "浙江大学".equals(student.getSchool()))
            .collect(Collectors.toList());
    collect.forEach(System.out::println);
2.1.2 distinct 
    相当于sql中的去重 , 去重数据中的重复数字

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

    List<Integer> collect = list.stream().distinct().collect(Collectors.toList());

    collect.forEach(System.out::println);
2.1.3limit
limit操作也类似于SQL语句中的LIMIT关键字,不过limit返回包含前n个元素的流,当集合大小小于n时,则返回实际长度,比如下面的例子返回前两个学生:

List stu = students.stream()
.limit(2)
.collect(Collectors.toList());

    stu.forEach(s-> System.out.println(s));
2.1.4 sorted 
 该操作用于对流中元素进行排序,
 比如我们希望筛选出专业为土木工程的学生,并按年龄从小到大排序,筛选出年龄最小的两个学生,那么可以实现为:

    List<Student> stu = students.stream().sorted(Comparator.comparing(Student::getAge))
            .limit(2)
            .collect(Collectors.toList());

    stu.forEach(s-> System.out.println(s));

2.1.5 skip
skip操作与limit操作相反,如同其字面意思一样,是跳过前n个元素,比如我们希望找出排序在2之后的土木工程专业的学生,那么可以实现为:

        List<Student> stu = students.stream()
                .filter(student -> "土木工程".equals(student.getMajor()))
                .skip(2)
                .collect(Collectors.toList());

        stu.forEach(s-> System.out.println(s));
2.2 映射
在java8的流式处理中,主要包含两类映射操作:map和flatMap。
2.2.1 map
举例说明,假设我们希望筛选出所有专业为计算机科学的学生姓名,那么我们可以在filter筛选的基础之上,通过map将学生实体映射成为学生姓名字符串,具体实现如下:
   String stu = students.stream()
            .filter(s->s.getMajor().equals("计算机科学"))
            .map(Student::getName)
            .collect(Collectors.joining(", "));


    System.out.println(stu);

除了上面这类基础的map,java8还提供了mapToDouble(ToDoubleFunction<? super T> mapper),mapToInt(ToIntFunction<? super T> mapper),mapToLong(ToLongFunction<? super T> mapper),这些映射分别返回对应类型的流,java8为这些流设定了一些特殊的操作,比如我们希望计算所有专业为计算机科学学生的年龄之和,那么我们可以实现如下:

    Integer stu = students.stream()
            .filter(s->s.getMajor().equals("计算机科学"))
            .mapToInt(Student::getAge)
            .sum();

    System.out.println(stu);
2.2.2 flatMap
flatMap与map的区别在于 flatMap是将一个流中的每个值都转成一个个流,然后再将这些流扁平化成为一个流 。举例说明,假设我们有一个字符串数组String[] strs = {"java", "C#", "C++", "php", "javascrip","java"};,我们希望输出构成这一数组的所有非重复字符,那么我们可能首先会想到如下实现:

        String[] strs = {"java", "C#", "C++", "php", "javascrip","java"};


        List<String> collect = Stream.of(strs)
                .map(s -> s.split(""))
                .distinct()
                .flatMap(Arrays::stream)
                .collect(Collectors.toList());

        collect.forEach(s-> System.out.println(s+": "));


三. 终端操作

3.1.1allMatch
allMatch用于检测是否全部都满足指定的参数行为,如果全部满足则返回true,例如我们希望检测是否所有的学生都已满18周岁,那么可以实现为:
    boolean isAdult = students.stream().allMatch(student -> student.getAge() >= 22);


    System.out.println(isAdult);
3.1.2anyMatch
anyMatch则是检测是否存在一个或多个满足指定的参数行为,如果满足则返回true,例如我们希望检测是否有来自武汉大学的学生,那么可以实现为:

        boolean isAdult = students.stream().anyMatch(student -> student.getAge() >= 22);


        System.out.println(isAdult);



3.1.3 noneMathch
noneMatch用于检测是否不存在满足指定行为的元素,如果不存在则返回true,例如我们希望检测是否不存在专业为计算机科学的学生,可以实现如下:

boolean noneCs = students.stream().noneMatch(student -> “计算机科学”.equals(student.getMajor()));


3.2 归约

前面的例子中我们大部分都是通过collect(Collectors.toList())对数据封装返回,如我的目标不是返回一个新的集合,而是希望对经过参数化操作后的集合进行进一步的运算,那么我们可用对集合实施归约操作。java8的流式处理提供了reduce方法来达到这一目的。

前面我们通过mapToInt将Stream<Student>映射成为IntStream,并通过IntStream的sum方法求得所有学生的年龄之和,实际上我们通过归约操作,也可以达到这一目的,实现如下:
    Integer reduce = students.stream().map(Student::getAge).reduce(0, (a, b) -> a + b);

    System.out.println(reduce);

“`
持续精进………………………………….

阅读更多
换一批

没有更多推荐了,返回首页