欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java8新特性(三)集合之 Stream 流式操作

程序员文章站 2022-06-12 10:30:11
stream就像一条大管套小管的管道,我们不用关心它的源头在哪里,只需要知道我们可以在目的地(海?)可以收集到来自五湖四海的一样。一般情况下,我们也不用管这管道源头在哪里,只需要拿到我们需要的东西。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之, Stream API 提供了一种高效且易于使用的处理数据的方式。参考JDK8新特性(三):集合之 Stream 流式操作JDK8辅助学习(四):Strea....

stream就像一条大管套小管的管道,我们不用关心它的源头在哪里,只需要知道我们可以在目的地(海?)可以收集到来自五湖四海的一样。一般情况下,我们也不用管这管道源头在哪里,只需要拿到我们需要的东西。
使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之, Stream API 提供了一种高效且易于使用的处理数据的方式。


参考
JDK8新特性(三):集合之 Stream 流式操作

JDK8辅助学习(四):Stream流 collect() 方法的详细使用介绍



一、感受一下

Stream是什么?

是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算! ”

stream的特点:

  • Stream 自己不会存储元素。
  • Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
  • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

需求:挑选出年龄大于12岁的学生,并按照年龄从大到小的顺序将它们排成一排。

package com._520xuzai.stream;

import java.util.List;

/**
 * 学生对象
 */
public class Student {

    private Integer id;

    private int age;

    private String name;

    private List<String> courses;

    public Student(int age, String name, List<String> courses) {
        this.age = age;
        this.name = name;
        this.courses = courses;
        this.id = 1;
    }

    public Student(Integer id, int age, String name, List<String> courses) {
        this.id = id;
        this.age = age;
        this.name = name;
        this.courses = courses;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<String> getCourses() {
        return courses;
    }

    public void setCourses(List<String> courses) {
        this.courses = courses;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", courses=" + courses +
                '}';
    }
}

具体实现

package com._520xuzai.stream;

import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

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


    static {
        for (int i=0;i<20;i++){
            List<String> courses = new ArrayList<>();
            int age = 5+i;
            courses.add("语文");
            courses.add("外语"+age);
            courses.add("高数"+age);
            students.add(new Student(age,"张三_"+age,courses));
        }
    }


    //需求:挑选出年龄大于12岁的学生,并2.按照年龄从大到小的顺序将它们排成一排。
    @Test
    public void testFunction() {

        //Java7(集合)实现
        //使用迭代器筛选元素
        List<Student> studentList = new ArrayList<Student>();
        for (Student student : students) {
            if (student.getAge() > 12) {
                studentList.add(student);
            }
        }

        //使用匿名类对学生进行年龄排序(从大到小)
        Collections.sort(studentList, new Comparator<Student>() {
            public int compare(Student s1, Student s2) {
                return Integer.compare(s2.getAge(), s1.getAge());
            }
        });

        //将排好序的学生排成一排(放到一起)
        List<String> lastStudentList = new ArrayList<>();
        for (Student s : studentList) {
            lastStudentList.add(s.getName());
        }

        System.out.println("普通方式"+lastStudentList);

        //Java8(Stream)实现
        List<String> lastStudentList2 =
                students.stream()
                        .filter(s -> s.getAge() > 12)//挑选出年龄大于12岁的学生
                        .sorted(Comparator.comparing(Student::getAge).reversed())//按照年龄从大到小(默认从小到大)进行排序
                        .map(Student::getName)//提取满足要求的学生的名字
                        .collect(Collectors.toList());//将名字保存到List中

        System.out.println("stream的方式"+lastStudentList2);

    }

    }

二、Stream流常用方法


    /**
     * forEach() 方法用来遍历流中的数据,是一个终结方法。该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。
     * @param action
     */
    void forEach(Consumer<? super T> action);

    /**
     * count() 方法,用来统计集合中的元素个数,是一个终结方法。该方法返回一个 long 值代表元素个数
     * @return
     */
    long count();


    /**
     *  filter() 方法,用于过滤数据,返回符合过滤条件的数据,是一个非终结方法。
     *  我们可以通过 filter() 方法将一个流转换成另一个子集流。
     *  该接口接收一个 Predicate 函数式接口参数(可以是一个 Lambda 或 方法引用) 作为筛选条件
     * @param predicate
     * @return
     */
    Stream<T> filter(Predicate<? super T> predicate);

    /**
     * limit() 方法,用来对 Stream 流中的数据进行截取,只取用前 n 个,是一个非终结方法。
     * 参数是一个 long 型,如果集合当前长度大于参数则进行截取,否则不进行操作。
     * @param maxSize
     * @return
     */
    Stream<T> limit(long maxSize);

    /**
     * 如果希望跳过前几个元素,去取后面的元素,则可以使用 skip()方法,获取一个截取之后的新流
     * @param n
     * @return
     */
    Stream<T> skip(long n);


    /**
     * map() 方法,可以将流中的元素映射到另一个流中。
     * 即:可以将一种类型的流转换为另一种类型的流
     * 可以将当前流中的T类型数据转换为另一种R类型的流
     * @param mapper
     * @param <R>
     * @return
     */
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);


    /**
     *  flatMap 的使用,同 map 类似。
     *  map只是一维 1对1 的映射,而flatMap可以将一个2维的集合映射成一个一维,相当于它映射的深度比map深了一层。
     * @param mapper
     * @param <R>
     * @return
     */
    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

    /**
     * 根据元素的自然规律排序
     * @return
     */
    Stream<T> sorted();
    /**
     * 根据比较器指定的规则排序
     * @param comparator
     * @return
     */
    Stream<T> sorted(Comparator<? super T> comparator);

    /**
     * distinct() 方法,可以用来去除重复数据
     * @return
     */
    Stream<T> distinct();



    /**
     * allMatch 全匹配(匹配所有,所有元素都需要满足条件-->返回true)
     * @param predicate
     * @return
     */
    boolean allMatch(Predicate<? super T> predicate);

    /**
     * anyMatch 匹配某个元素(只要有一个元素满足条件即可-->返回true)
     * @param predicate
     * @return
     */
    boolean anyMatch(Predicate<? super T> predicate);

    /**
     * noneMatch 匹配所有元素(所有元素都不满足指定条件-->返回true)
     * @param predicate
     * @return
     */
    boolean noneMatch(Predicate<? super T> predicate);

    /**
     * findFirst() 、findAny() 方法,都是用来查找 Stream 流中的第一个元素。
     * 返回值为 Optional<T>,意味着有可能找到也有可能找不到的情况
     * @return
     */
    Optional<T> findFirst();
    Optional<T> findAny();

    /**
     * max() 和 min() 方法,用来获取 Stream 流中的最大值和最小值。
     * 该接口需要一个 Comparator 函数式接口参数,根据指定排序规则来获取最大值,最小值。
     *为了保证数据的准确性,此处排序规则需要是升序排序。
     * 因为:max() 方法获取的是排序后的最后一个值,min() 方法获取的是排序后的第一个值。
     * @param comparator
     * @return
     */
    Optional<T> max(Comparator<? super T> comparator);
    Optional<T> min(Comparator<? super T> comparator);

三、实践

1、flatmap与map的区别

 @Test
    public void testFlatMap() {

//        Stream<Student> studentStream = students.stream().filter(s -> (s.getAge() > 23 || s.getAge() > 23));


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

        List<String> courses1 = new ArrayList<>();
        courses1.add("语文");
        courses1.add("外语");
        courses1.add("高数");
        studentList.add(new Student(18,"张三",courses1));
        courses1.clear();
        courses1.add("语文");
        courses1.add("物理");
        courses1.add("化学");
        studentList.add(new Student(19,"李四",courses1));

        //使用map,需要多次forEach

        Stream<List<String>> listStream = studentList.stream().map(student -> student.getCourses());
        System.out.println("map1----------");
        studentList.stream().map(student -> student.getCourses()).forEach(System.out::println);
        System.out.println("map2----------");
        studentList.stream().map(student -> student.getCourses().stream().map(s->s.replace("语文","政治"))).forEach(x -> System.out.println(Arrays.toString(x.toArray())));
        System.out.println("map3----------");
        studentList.stream().map(student->student.getCourses()).forEach(courses -> courses.forEach(System.out::println));

        //使用 flatMap,一次forEach即可
        Stream<String> stringStream = studentList.stream().flatMap(student -> student.getCourses().stream());
        System.out.println("flatmap1----------");
        studentList.stream().map(student->student.getCourses()).flatMap(student->student.stream()).forEach(System.out::println);

        System.out.println("flatmap2----------");
        studentList.stream().map(student->student.getCourses()).flatMap(student->student.stream().map(s -> s.replace("语文","政治"))).forEach(System.out::println);

        System.out.println("flatmap3----------");
        studentList.stream().flatMap(student->student.getCourses().stream()).forEach(System.out::println);

         System.out.println("flatmap4----------");
        studentList.stream().flatMap(student->student.getCourses().stream().map(s->s.replace("语文","政治"))).forEach(System.out::println);


    }

输出结果:


map1----------
[语文, 物理, 化学]
[语文, 物理, 化学]
map2----------
[政治, 物理, 化学]
[政治, 物理, 化学]
map3----------
语文
物理
化学
语文
物理
化学
flatmap1----------
语文
物理
化学
语文
物理
化学
flatmap2----------
政治
物理
化学
政治
物理
化学
flatmap3----------
语文
物理
化学
语文
物理
化学
flatmap4----------
政治
物理
化学
政治
物理
化学

2、Collect相关操作

    @Test
    public void testCollect() {

//        Stream<Student> studentStream = students.stream().filter(s -> (s.getAge() > 23 || s.getAge() > 23));


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

        List<String> courses1 = new ArrayList<>();
        courses1.add("语文");
        courses1.add("外语");
        courses1.add("高数");
        studentList.add(new Student(1,18, "张三", courses1));
        courses1.clear();
        courses1.add("语文");
        courses1.add("物理");
        courses1.add("化学");
        studentList.add(new Student(2,18, "李四", courses1));

        //转map,需要指定key和value,Function.identity()表示当前的Emp对象本身
        studentList.stream().collect(Collectors.toMap(Student::getId,Function.identity())).forEach((x,y) -> System.out.println("id="+x+"  __实体信息__"+y));
        //分组操作
        studentList.stream().collect(Collectors.groupingBy(Student::getAge)).forEach((x,y) -> System.out.println("age="+x+"  __实体信息__"+y));
        //分片
        studentList.stream().collect(Collectors.partitioningBy(student -> student.getId() > 1)).forEach((x,y) -> System.out.println("根据id进行分区"+x+"  __实体信息__"+y));

    }

输出:

id=1  __实体信息__Student{age=18, name='张三', courses=[语文, 物理, 化学]}
id=2  __实体信息__Student{age=18, name='李四', courses=[语文, 物理, 化学]}
age=18  __实体信息__[Student{age=18, name='张三', courses=[语文, 物理, 化学]}, Student{age=18, name='李四', courses=[语文, 物理, 化学]}]
根据id进行分区false  __实体信息__[Student{age=18, name='张三', courses=[语文, 物理, 化学]}]
根据id进行分区true  __实体信息__[Student{age=18, name='李四', courses=[语文, 物理, 化学]}]

本文地址:https://blog.csdn.net/qq_39151461/article/details/112099902