• Stream流


    Stream流

    3.1 概述

    Stream是Java8 API的新成员,它允许以声明性方式处理数据集合 。

    特点:

    • 代码简洁函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环。
    • 多核友好:Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下方法。

    3.2准备阶段

    book.java(书籍类)

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /**
     * @author 尹稳健~
     * @version 1.0
     * @time 2022/8/25
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Book {
        private Long id;
        private String name;
        private String category;
        private Integer score;
        private String intro;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    author.java (作者类)

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import java.util.List;
    /**
     * @author 尹稳健~
     * @version 1.0
     * @time 2022/8/25
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Author {
        private Long id;
        private String name;
        private Integer age;
        private String intro;
        private List<Book> books;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    生成简单数据的方法

    private static List<Author> getAuthors(){
        Author author1 = new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
        Author author2 = new Author(2L, "亚索", 15, "狂风也追逐不上他的思考速度", null);
        Author author3 = new Author(3L, "易", 14, "是这个世界限制了他的思维", null);
        Author author4 = new Author(4L, "易", 14, "是这个世界限制了他的思维", null);
    
        List<Book> book1 = new ArrayList<>();
        List<Book> book2 = new ArrayList<>();
        List<Book> book3 = new ArrayList<>();
    
        book1.add(new Book(1L,"刀的两侧是光明与黑暗","哲学,爱情",88,"用一把刀划分爱恨"));
        book1.add(new Book(2L,"一个人不能死在同一把刀下","个人成长,爱情",99,"讲述如何从失败到成功"));
    
        book2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你用思维去领略世界的尽头"));
        book2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你用思维去领略世界的尽头"));
        book2.add(new Book(4L,"吹或不吹","个人传记,爱情",56,"一个哲学家的恋爱观注定很难把他"));
    
        book3.add(new Book(5L,"你的剑就是我的剑","爱情",56,"无法想象一个武者能对他的伴侣"));
        book3.add(new Book(6L,"风与剑","个人传记",100,"两个哲学家的灵魂和肉体的碰撞会激起怎么样的"));
        book3.add(new Book(6L,"风与剑","个人传记",100,"两个哲学家的灵魂和肉体的碰撞会激起怎么样的"));
    
        author1.setBooks(book1);
        author2.setBooks(book2);
        author3.setBooks(book3);
        author4.setBooks(book3);
    
        ArrayList<Author> authorList = new ArrayList<>(Arrays.asList(author1,author2,author3,author4));
        return authorList;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    3.3快速入门

    3.3.1 需求

    从getAuthors方法获取到的集合,进行筛选过滤,获取所有年龄小于18岁的作者,去重

    3.3.2实现

    使用普通方法实现:

    List<Author> authors = getAuthors();
    ArrayList<Author> authors_lt = new ArrayList<>();  // 新的数组保存作者
    for (Author author : authors) {
        if (author.getAge()<18){
            authors_lt.add(author);
        }
    }
    Set<Author> set = new HashSet<>();	// 使用set去重
    for (Author author : authors_lt) {
        set.add(author);
    }
    System.out.println(set);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    使用Stream流的方式实现:

    考虑到可能会看不懂,所以匿名内部类也留在这:

    List<Author> authors = getAuthors();
    // 获取所有年龄小于18岁的作者,去重
    authors.stream() // 将集合转为流
        .distinct()  // 去重
        .filter(new Predicate<Author>() {
            @Override
            public boolean test(Author author) {
                return author.getAge() < 18;
            }
        }) // 过滤条件 Predicate
        .forEach(new Consumer<Author>() {
            @Override
            public void accept(Author author) {
                System.out.println(author);
            }
        }); //遍历打印 Consumer
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    简化:

    List<Author> authors = getAuthors();
    // 获取所有年龄小于18岁的作者,去重
    authors.stream() // 将集合转为流
        .distinct()  // 去重
        .filter(author -> author.getAge() < 18) // 过滤条件 Predicate
        .forEach(author -> System.out.println(author)); //遍历打印 Consumer
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    idea自带的debug:

    在这里插入图片描述

    点开后不要急,他在计算中

    在这里插入图片描述

    过了一会就有了

    在这里插入图片描述

    去重

    在这里插入图片描述

    过滤:

    在这里插入图片描述

    3.4常用操作

    3.4.1创建流

    单列集合(Collection、List):集合对象.stream()

    List<Author> authors = getAuthors();
    Stream<Author> stream = authors.stream();
    
    • 1
    • 2

    数组:Arrays.stream(数组)或者使用Stream.of来创建

    Integer[] arr = {1,2,3,4};
    Stream<Integer> stream = Arrays.stream(arr);
    
    Stream<Integer> stream = Stream.of(arr);
    
    • 1
    • 2
    • 3
    • 4

    双列集合:转换为单列集合后在创建

    Stream<Map.Entry<String, Object>> stream = map.entrySet().stream();
    
    • 1
    3.4.2中间操作
    filter

    ​ 可以对流中的数据进行过滤,符合条件的留在流中

    例如:打印所有姓名长度大于1的作者的名字

    List<Author> authors = getAuthors();
    // 打印所有姓名长度大于1的作者的名字
    authors.stream()    // 将数据放入stream流中
        .filter(author -> {return author.getName().length()>1;})     // 过滤姓名长度大于1的作者的名字
        .forEach(author -> System.out.println(author.getName()));     // 终结操作
    
    • 1
    • 2
    • 3
    • 4
    • 5
    map

    ​ 可以对流进行计算或者转换

    例如:打印所有作家的名字

    List<Author> authors = getAuthors();
    // 打印所有作家的名字
    authors.stream()
        .map(new Function<Author, String>() {
            @Override
            public String apply(Author author) {	
                return author.getName();
            }
        })    // 取出所有的名字
        .forEach(s -> System.out.println(s));   // 进行打印
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    使用简化的lambda表达式:

    List<Author> authors = getAuthors();
    // 打印所有作家的名字
    authors.stream()
        .map(author -> author.getName())    // 取出所有的名字
        .forEach(s -> System.out.println(s));   // 进行打印
    
    • 1
    • 2
    • 3
    • 4
    • 5
    distinct

    ​ 去除流中重复的数据

    例如:打印所有作家的名字,要求其中不能有重复的数据

    List<Author> authors = getAuthors();
    // 打印所有作家的名字,要求其中不能有重复的数据
    authors.stream()
        .distinct()	// 去重
        .forEach(author -> System.out.println(author.getName()));	// 遍历打印
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注意:distinct方法是以来Obejct的equals方法来判断是否是相同的对象。所以需要重写equals方法

    sorted

    ​ 可以对流中的数据进行排序

    例如:对流中的数据进行年龄降序排序,且不能有相同重复的数据

    List<Author> authors = getAuthors();
    authors.stream()
        .distinct()
        .sorted(new Comparator<Author>() {  
            @Override
            public int compare(Author o1, Author o2) {
                return o2.getAge() - o1.getAge();	// o1 - o2 升序 , o2 - o1 降序
            }
        })
        .forEach(author -> System.out.println(author));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用lambda表达式:

    List<Author> authors = getAuthors();
    authors.stream()
        .distinct()
        .sorted((o1, o2) -> o2.getAge() - o1.getAge())
        .forEach(author -> System.out.println(author));
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注意:如果调用空参的sorted方法需要实现Comparator接口,重写方法

    limit

    ​ 可以设置流的最大长度,超过的部分被抛弃

    例如:对流中的数据按照年龄降序排序,且不能有重复的数据,然后打印其中年龄最大的2个作家的姓名

    List<Author> authors = getAuthors();
    authors.stream()
        .distinct()
        .sorted((o1, o2) -> o2.getAge() - o1.getAge())
        .limit(2)
        .forEach(author -> System.out.println(author));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    skip

    ​ 跳过流中的前n个数据,返回剩下的数据

    例如:打印除了年龄最大的作家外的所有作家,不能有重复的数据,且按照年龄的降序排序

    List<Author> authors = getAuthors();
    authors.stream()
        .distinct()
        .sorted((o1, o2) -> o2.getAge() - o1.getAge())
        .skip(1)
        .forEach(author -> System.out.println(author));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    flatMap

    ​ map只能把一个对象转换成另一个对象来作为流中的数据,而flatMap可以把一个对象转化为多个对象作为流中的数据

    例一:打印所有书籍的名称,要求重复数据去重

    // 打印所有书籍的名称,要求重复数据去重
    authors.stream()
        .distinct()
        .flatMap(new Function<Author, Stream<Book>>() {
            @Override
            public Stream<Book> apply(Author author) {
                return author.getBooks().stream().distinct();
            }
        })
        .forEach(new Consumer<Book>() {
            @Override
            public void accept(Book book) {
                System.out.println(book);
            }
        });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    使用简化的lambda表达式:

    // 打印所有书籍的名称,要求重复数据去重
    authors.stream()
        .distinct()
        .flatMap(author -> author.getBooks().stream())
        .distinct()
        .forEach(book -> System.out.println(book));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    例二:打印数据的所有分类,按分类去重,不能出现这种格式:哲学,爱情

    List<Author> authors = getAuthors();
    authors.stream()
        .flatMap(author -> author.getBooks().stream())
        .distinct()
        .flatMap(book -> Arrays.asList(book.getCategory().split(",")).stream())
        .distinct()
        .forEach(s -> System.out.println(s));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    3.4.3终结操作
    forEach

    ​ 对流中的数据进行遍历操作,我们通过传入的参数去指定对遍历到的数据进行具体操作

    例子:列出所有作家的名字

    List<Author> authors = getAuthors();
    authors.stream()
        .distinct()
        .forEach(author -> System.out.println(author.getName()));
    
    • 1
    • 2
    • 3
    • 4
    count

    ​ 可以用来获取当前流中数据的个数

    例子:打印这些作家的所出书籍的数量,去重

    List<Author> authors = getAuthors();
    authors.stream()
        .distinct()
        .map(author -> author.getBooks())
        .distinct()
        .count();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    max&min

    ​ 可以用来获取流中的最值

    例子:分别获取作家书籍中评分的最高和最低值

    List<Author> authors = getAuthors();
    Optional<Integer> max = authors.stream()
        .flatMap(author -> author.getBooks().stream())
        .distinct()
        .map(book -> book.getScore())
        .max((o1, o2) -> o1 - o2);
    System.out.println(max);
    
    Optional<Integer> min = authors.stream()
        .flatMap(author -> author.getBooks().stream())
        .distinct()
        .map(book -> book.getScore())
        .min((o1, o2) -> o1 - o2);
    System.out.println(min);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    collect

    ​ 把当前流转换为一个集合

    例一:获取一个存放所有作者姓名的List集合,注意去重

    List<Author> authors = getAuthors();
    //        例一:获取一个存放所有作者姓名的List集合,注意去重
    List<String> list = authors.stream()
        .map(author -> author.getName())
        .distinct()
        .collect(Collectors.toList());
    System.out.println(list);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    例二:获取一个全是书名的集合,去重

    List<Author> authors = getAuthors();
    //        例二:获取一个全是书名的集合,去重
    Set<String> set = authors.stream()
        .flatMap(author -> author.getBooks().stream())
        .map(book -> book.getName())
        .collect(Collectors.toSet());
    System.out.println(set);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    例三:获取一个map集合,map的key为作者名,value为List

    List<Author> authors = getAuthors();
    //        例三:获取一个map集合,map的key为作者名,value为List
    Map<String, List<Book>> map = authors.stream()
        .distinct()
        .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
    System.out.println(map);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意collect()参数是传入一个Collectors工具类

    查找与匹配
    anyMatch

    ​ 可以用来判断是否任意数据符合匹配条件的数据,结果为Boolean类型

    例子:判断是否有年龄在25岁以上的

    List<Author> authors = getAuthors();
    boolean b = authors.stream()
        .anyMatch(author -> author.getAge() > 25);
    System.out.println(b);
    
    • 1
    • 2
    • 3
    • 4
    allMatch

    ​ 可以用来判断是否任意数据符合匹配条件的数据,结果为Boolean类型,全符合时为true,有一条数据为false那就是false

    例子:判断是否所有作家都是成年人?

    List<Author> authors = getAuthors();
    boolean b1 = authors.stream()
        .allMatch(author -> author.getAge() > 17);
    System.out.println(b1);
    
    • 1
    • 2
    • 3
    • 4
    noneMatch

    ​ 可以用来判断是否任意数据符合匹配条件的数据,结果为Boolean类型,全不符合为true,否则false

    例子:判断作家是否都没有超过100岁

    List<Author> authors = getAuthors();
    boolean b2 = authors.stream()
        .noneMatch(author -> author.getAge() > 100);
    System.out.println(b2);
    
    • 1
    • 2
    • 3
    • 4
    findAny

    ​ 获取流中任意个一条数据,该方法没办法保证获取的一定是流的第一条数据

    例子:获取任意个大于18的作家,如果存在就输出他的名字

    List<Author> authors = getAuthors();
    Optional<Author> any = authors.stream()
        .filter(author -> author.getAge() > 18)
        .findAny();
    System.out.println(any);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    findFirst

    ​ 获取流中的第一个条数据

    例子:获取年龄最小的作家,并输出他的名字

    List<Author> authors = getAuthors();
    Optional<Author> first = authors.stream()
        .sorted((o1, o2) -> o1.getAge() - o2.getAge())
        .findFirst();
    System.out.println(first);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    reduce归并

    ​ 对流中的数据按照你指定的计算方式计算出一个结果

    ​ reduce的作用是把stream中的数据组合起来,我们可以传入一个初始值,它会按照我们的计算方式依次拿到流中的数据和初始化值进行运算,计算结果再和每次遍历的数据计算。

    ​ Optional reduce(BinaryOperator accumulator);底层实现:

    boolean foundAny = false;
    T result = null;
    for (T element : this stream) {
        if (!foundAny) {
         	foundAny = true;
             result = element;
        }
        else
         	result = accumulator.apply(result, element);
        }
    return foundAny ? Optional.of(result) : Optional.empty();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    T reduce(T identity, BinaryOperator accumulator);底层实现:

    T result = identity;
    for (T element : this stream)
        result = accumulator.apply(result, element)
    return result;
    
    • 1
    • 2
    • 3
    • 4

    U reduce(U identity,
    ​ BiFunction accumulator,
    ​ BinaryOperator combiner);底层源码

    U result = identity;
    for (T element : this stream)
    	result = accumulator.apply(result, element)
    return result;
    
    • 1
    • 2
    • 3
    • 4

    例一:使用reduce求所有作者的年龄和

    List<Author> authors = getAuthors();
    // 使用reduce求所有作者的年龄和
    Integer reduce = authors.stream()
        .map(author -> author.getAge())
        .reduce(0, (integer, integer2) -> integer + integer2);
    System.out.println(reduce);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    例二:使用reduce求所有作家年龄最大的

    List<Author> authors = getAuthors();
    // 使用reduce求所有作家年龄最大的
    Integer max = authors.stream()
        .map(author -> author.getAge())
        .reduce(Integer.MIN_VALUE, (result, element) -> result > element ? result : element);
    System.out.println(max);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    例三:使用reduce求所有作者的最小值

    List<Author> authors = getAuthors();
    // 使用reduce求所有作家年龄最小的
    Integer min = authors.stream()
        .map(author -> author.getAge())
        .reduce(Integer.MAX_VALUE, (result, element) -> result < element ? result : element);
    System.out.println(min);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    第二种方式:

    Optional<Integer> min1 = authors.stream()
        .map(author -> author.getAge())
        .reduce((integer, integer2) -> integer < integer2 ? integer : integer2);
    System.out.println(min1);
    
    • 1
    • 2
    • 3
    • 4
    3.5注意事项:
    • 惰性求值(如果没有终结操作,那么他的中间操作都不会执行)
    • 流是一次性的(一旦这个流使用了终结操作,那么这个流就不会在被使用)
    • 不会影响原数据(我们可以对流进行很多处理,但是正常情况下是不会影响到原来的数据)
  • 相关阅读:
    leetcode-99.恢复二叉搜索树
    不看后悔,appium自动化环境完美搭建
    洛谷 P4815 狼人游戏 题解
    C专家编程 第7章 对内存的思考 7.4 cache存储器
    每天五分钟计算机视觉:如何在现有经典的卷积神经网络上进行微调
    数据结构--哈希表(Hash Table)
    重生之 SpringBoot3 入门保姆级学习(19、场景整合 CentOS7 Docker 的安装)
    在linux上不依赖于Nignx等服务器部署ASP.NET Core 7.0 WebAPI
    HttpClient / Http客户端
    如何记录每天的工作日程?电脑手机通用的日程管理软件
  • 原文地址:https://blog.csdn.net/weixin_46073538/article/details/126843373