Stream流

文章介绍了Java中使用流(Stream)进行数据处理的方法,包括去重、过滤、映射、排序、限制、跳过、扁平化等操作,以及如何在Author和Book实体类中应用这些操作。示例代码展示了如何处理Author和Book集合,如提取18岁以下作者的名字,以及处理书籍信息等。
摘要由CSDN通过智能技术生成

概述

实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Book {
    //id
    private Long id;
    //书名
    private String name ;
    //分类
    private String category;
    //评分
    private Integer score;
    //简介
    private String intro;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode //用于后期的去重使用
public class Author {
    private Long id;//id
    private String name;//姓名
    private Integer age;//年龄
    private String intro;//简介
    private List<Book> books;//作品
}

测试类

private static List<Author> getAuthors() {
    //数据初始化
    Author author = new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
    Author author2 = new Author(2L, "亚拉索", 15, "狂风也追逐不上他的思考速度", null);
    Author author3 = new Author(3L, "易", 14, "是这个世界在限制他的思维", null);
    Author author4 = new Author(3L, "易", 14, "是这个世界在限制他的思维", null);
    //书籍列表
    List<Book> books1 = new ArrayList<>();
    List<Book> books2 = new ArrayList<>();
    List<Book> books3 = new ArrayList<>();


    books1.add(new Book(1L, "刀的两侧是光明与黑暗", "哲学,爱情",88,"用一把刀划分了爱恨"));
    books1.add(new Book(2L,"一个人不能死在同一把刀下" , "个人成长,爱情" ,99, "讲述如何从失败中明悟真理"));

    books2.add(new Book(3L, "那风吹不到的地方", "哲学" ,85, "带你用思维去领略世界的尽头"));
    books2.add(new Book(3L, "那风吹不到的地方", "哲学" ,85, "带你用思维去领略世界的尽头"));
    books2.add(new Book(4L,"吹或不吹","爱情,个人传记",56,"一个哲学家的恋爱观注定很难把他所在的时代理解"));

    books3.add(new Book(5L, "你的剑就是我的剑","爱情",56,"无法想象一个武者能对他的伴侣这么的宽容"));
    books3.add(new Book(6L, "风与剑","个人传记",100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));
    books3.add(new Book(6L, "风与剑","个人传记",100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));
    author.setBooks(books1);
    author2.setBooks(books2);
    author3.setBooks(books3);
    author4.setBooks(books3);

    ArrayList<Author> authorList = new ArrayList<>(Arrays.asList(author, author2, author3, author4));
    return authorList;
}

问题:去重并打印18岁以下的名字

public static void main(String[] args) {
    List<Author> authors = getAuthors();
    authors.stream()
            .distinct()
            .filter((author -> author.getAge()<18))
            .forEach(author -> System.out.println(author.getName()));
}

创建流

单列集合集合对象.stream()

Stream<Author> stream = list.stream();

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

Integer[] arr = {1,2,3,4,5};
//方法一
Stream<Integer> stream1 = Arrays.stream(arr);
//方法二
Stream<Integer> stream2 = Stream.of(arr);

双列集合:转换成单列集合在创建

public static void main(String[] args) {
    HashMap<String, Integer> hashMap = new HashMap<>();
    hashMap.put("小明",11);
    hashMap.put("小红",11);
    hashMap.put("小张",12);

    Set<Map.Entry<String, Integer>> entries = hashMap.entrySet();
    Stream<Map.Entry<String, Integer>> stream = entries.stream();
    stream.distinct()
            .filter(entry -> entry.getValue() >11)
            .forEach(System.out::println);
}

在这里插入图片描述

中间操作

filter

可以对流中的元素进行条件过滤,符合过滤条件的才能继续留在流中。

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

List<Author> authors = getAuthors();

authors.stream()
        .distinct()
        .filter((author -> author.getName().length() > 1))
        .forEach(author -> System.out.println(author.getName()));

map

可以把流中的元素进行计算或转换

例如:

//打印所有作家的姓名
List<Author> authors = getAuthors();

authors.stream()
        .map(author -> {
            author.setName("张三");
            return author;//是将对象的name都改成 张三 但是还是返回的List对象
        })
        .map(author -> author.getName())//但是这里就相当于 将原来传入的List<Author> 变为List<String>
        .forEach(name -> System.out.println(name instanceof String));//将括号中的换成name会全部打印张三

在这里插入图片描述

distinct

可以取出流中的重复元素

List<Author> authors = getAuthors();

authors.stream()
        .distinct()
        .forEach(author -> System.out.println(author.getName()));

结果:
在这里插入图片描述

注意: distinct方法是依赖Object的equals方法来判断是否是相同对象的。所以需要注意重写equals方法。如果不重写就相当于== ,对象就是比较的地址值。

sorted

这个方法有两种形式:
在这里插入图片描述
① 如果调用空参的sorted()方法,需要流中的元素实现Comparable,并且重写compareTo方法

@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode //用于后期的去重使用
public class Author implements Comparable<Author>{
    private Long id;//id
    private String name;//姓名
    private Integer age;//年龄
    private String intro;//简介
    private List<Book> books;//作品


    @Override
    public int compareTo(Author o) {
        return this.getAge() - o.getAge();
    }
}
private static void test03() {
    List<Author> authors = getAuthors();

    authors.stream()
            .distinct()
            .sorted()
            .forEach(author -> System.out.println(author.getAge()));
}

② 带参数的sorted我们用的比较多

private static void test03() {
    List<Author> authors = getAuthors();

    authors.stream()
            .distinct()
            .sorted((o1,o2)->o1.getAge()-o2.getAge())
            .forEach(author -> System.out.println(author.getAge()));
}

结果:
在这里插入图片描述

limit

跟mysql中limit一个意思,一般配合sorted使用

例如:
打印年龄最大的两个作家并且去重

List<Author> authors = getAuthors();

authors.stream()
        .distinct()
        .sorted((o1,o2)->o2.getAge()-o1.getAge())
        .limit(2)
        .forEach(author -> System.out.println(author.getName()));

结果:
在这里插入图片描述

skip

跳过前n个元素

例如:
打印除了年龄最大的作家外的其他作家,要求不能有重复元素,并且按照年龄降序排序。

private static void test04() {
    List<Author> authors = getAuthors();

    authors.stream()
            .distinct()
            .sorted((o1,o2)->o2.getAge()-o1.getAge())
            .skip(1)
            .forEach(author -> System.out.println(author.getName()));
}

结果:
在这里插入图片描述

flatMap

map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素。

例如:
打印所有书籍的名字。要求对重复的元素进行去重

List<Author> authors = getAuthors();

authors.stream()
        .flatMap(author -> author.getBooks().stream())
        .distinct()
        .forEach(book -> System.out.println(book.getName()));

结果:
在这里插入图片描述

//打印现有数据的所有分类。要求对分类进行去重。不能出现这种格式:哲学,爱情
List<Author> authors = getAuthors();

authors.stream()
        .flatMap(author -> author.getBooks().stream())
        .distinct()
        .flatMap(book -> Arrays.stream(book.getCategory().split(",")))
        .distinct()
        .forEach(category -> System.out.println(category));

结果:
在这里插入图片描述
map和flatmap 的区别

map:map方法返回的是一个object,map将流中的当前元素替换为此返回值;
flatMap:flatMap方法返回的是一个stream,flatMap将流中的当前元素替换为此返回流拆解的流元素;

网上有一个比较经典的例子
有二箱鸡蛋,每箱5个,现在要把鸡蛋加工成煎蛋,然后分给学生。
map做的事情:把二箱鸡蛋分别加工成煎蛋,还是放成原来的两箱,分给2组学生;
flatMap做的事情:把二箱鸡蛋分别加工成煎蛋,然后放到一起【10个煎蛋】,分给10个学生;

说白了map 只是改变了原来流中元素的类型(即我们的返回值),而flatmap 是将原来流中的每个值都换成另一个流,然后把所有流连接成一个新的流,这是我的理解

终结操作

forEach

对流中的元素进行遍历操作,我们通过传入的参数去指定对遍历到的元素进行什么具体操作。

例如:
输出所有的作家的名字

List<Author> authors = getAuthors();

authors.stream()
        .map(author -> author.getName())
        .distinct()
        .forEach(name -> System.out.println(name));

count

获取流中元素的个数

例如:
打印这些作家的所出书籍的数目,注意删除重复元素。

List<Author> authors = getAuthors();

long count = authors.stream()
        .flatMap(author -> author.getBooks().stream())
        .distinct()
        .count();
System.out.println(count);

max&min

求流中的最大最小值

例如:
分别获取这些作家的所出书籍的最高分和最低分并打印。

List<Author> authors = getAuthors();

Optional<Integer> maxScore = authors.stream()
        .flatMap(author -> author.getBooks().stream())
        .map(score -> score.getScore())
        .max((o1, o2) -> o1 - o2);//这里要实现Comparator的因为他不知道我们最大最小的规则是什么

System.out.println(maxScore.get());

collect

把当前流转换成一个集合

获取一个存放所有作者名字的List集合。
获取一个所有书名的set集合。
获取一个Map集合,map的key为作者名,value为List

### Stream的使用方法 #### 一、Stream概述 Java中的`Stream`并不是一种数据结构,而是通过管道传输元素的数据处理方式。这种机制允许开发者以声明式的方式对集合或数组进行各种高效的操作。 #### 二、获取Stream ##### 获取集合类型的Stream 对于已有的集合对象,可以直接调用其上的`.stream()`方法来创建对应的实例[^1]: ```java List<String> list = Arrays.asList("apple", "banana", "orange"); Stream<String> streamFromCollection = list.stream(); ``` ##### 获取数组类型的Stream 如果要基于数组构建,则可以通过静态工厂方法`Arrays.stream(T[] array)`实现转换: ```java int[] numbers = {1, 2, 3}; IntStream intStreamFromArray = Arrays.stream(numbers); ``` #### 三、中间操作与终结操作 在定义好输入源之后,就可以利用一系列丰富的API来进行过滤(filter)、映射(map)等变换动作了。值得注意的是,所有的这些所谓的“中间操作”,比如`filter()`, `map()`, 它们都是懒加载(lazy evaluation),即只有当遇到诸如`forEach()`, `collect()`, 或者`count()`这样的立即求值(eager evaluation)终端操作时才会真正触发整个水线的实际运行过程[^2]. 下面给出一段具体的例子展示如何组合多个阶段完成特定的任务: ```java Random random = new Random(); // 创建一个无限长度整数序列并截取前500项作为样本集 long count = Stream.generate(random::nextInt).limit(500) // 对每一条记录做简单打印预览(不影响后续程) .peek(item -> System.out.println("Peeked value:" + item)) // 设置筛选条件:仅保留大于十万以上的数值 .filter(item -> { System.out.print("Filtered value:"); return item > 100_000; }) // 统计满足上述所有约束后的剩余条目总数 .count(); System.out.printf("Total items greater than %d is %d\n", 100_000, count); ``` 这段程序展示了从生成随机数到最终统计符合条件的数量这一完整的链式调用链条。其中包含了两个重要的特性&mdash;&mdash;惰性和短路行为。由于采用了惰性评估策略,在没有到达最后一步之前不会有任何计算发生;而一旦碰到了像`limit(n)`这样具有短路特性的指令,则即使原生供给是无穷尽的也能安全停止迭代[^3].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值