Java essay Java essay
首页
  • Java基础
  • Java进阶
  • 设计模式
  • 多线程
  • Java你不知道的小事
  • Spring初识
  • Spring进阶
  • SpringBoot基础
  • SpringBoot进阶
  • 什么是微服务
  • SpringCloud全家桶
  • Dubbo
  • SpringCloud Alibaba
  • Vue
  • 小程序
  • 博客搭建
  • 数据库
  • python
  • 大数据
  • 性能分析优化
  • 中间件
  • 云原生
  • 面试
  • 外卖霸王餐
  • 打工人的带饭生活
  • 30岁我该怎么办
友链
关于我
GitHub (opens new window)

Mr.Fire

全栈工程师
首页
  • Java基础
  • Java进阶
  • 设计模式
  • 多线程
  • Java你不知道的小事
  • Spring初识
  • Spring进阶
  • SpringBoot基础
  • SpringBoot进阶
  • 什么是微服务
  • SpringCloud全家桶
  • Dubbo
  • SpringCloud Alibaba
  • Vue
  • 小程序
  • 博客搭建
  • 数据库
  • python
  • 大数据
  • 性能分析优化
  • 中间件
  • 云原生
  • 面试
  • 外卖霸王餐
  • 打工人的带饭生活
  • 30岁我该怎么办
友链
关于我
GitHub (opens new window)
  • Java基础

  • Java进阶

    • Jdk8-19新特性
    • 一文搞懂Java中的Stream流操作
      • 前言
      • 1 为什么需要 Stream
      • 2 Stream流的创建
      • 3 Stream的API
      • 4 使用
        • 4.1 遍历
        • 4.2 匹配
        • 4.3 过滤
        • 4.4 收集
        • 4.5 聚合
        • 4.6 映射
        • 4.7 排序
      • 总结
    • 一文详解ThreadLocal是什么
  • 设计模式

  • 多线程

  • 你不知道的小事

  • Java基础
  • Java进阶
Mr.Fire
2022-10-17
目录

一文搞懂Java中的Stream流操作

# 前言

Stream流操作是Java 8提供一个重要新特性,它允许开发人员以声明式方式处理集合,其核心类库主要改进了对集合类的 API和新增Stream操作。

你还可以把他理解成sql的视图,集合就相当于数据表中的数据,获取Stream流的过程就是确定数据表的属性和元数据的过程,元数据的每一个元素就是表中的数据,对Stream流进行操作的过程就是通过sql对这些数据进行查找、过滤、组合、计算、操作、分组等过程,获取结果就是sql执行完毕之后获取的结果视图一样,深入理解Stream流可以让我们使用更加简洁的代码获取自己想要的数据。

# 1 为什么需要 Stream

Stream 作为 Java 8 的一大亮点,它专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作。

Stream类中每一个方法都对应集合上的一种操作。将真正的函数式编程引入到Java中,能让代码更加简洁,极大地简化了集合的处理操作,提高了开发的效率和生产力。

# 2 Stream流的创建

  • 通过集合创建
  • 通过数组创建
  • 使用Stream的静态方法

Stream和parallelStream的简单区分

stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。

  • 串行流:适合存在线程安全问题、阻塞任务、重量级任务,以及需要使用同一事务的逻辑。
  • 并行流:适合没有线程安全问题、较单纯的数据处理任务。

# 3 Stream的API

归类

# 4 使用

使用前我们需要知道两个东西,一个是Optional,另外一个就是函数式接口

Optional:Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

作用:

  • 它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
  • Optional 类的引入很好的解决空指针异常

函数式接口:有且仅有一个抽象方法的接口,通过@FunctionalInterface注解声明

作用:

  • 是Lambda表达式的使用前提

  • 概念层面,为了表示接口就代表这个抽象方法,所以将名起为函数式接口

相信大家都了解,或多或少都用过,这里不做过多阐述。进入正题,结合日常开发遇到的一些场景,整理如下一些Stream使用场景。

构建一个产品List如下:

@Data
@AllArgsConstructor
public class Product {

    private Integer id;

    private String name;

    private String desc;

    /**
     * 价格
     */
    private BigDecimal price;

    /**
     * 类目
     */
    private String category;

    /**
     * 标签
     */
    private List<String> labels;

}

private static final List<Product> products = new ArrayList<>();

static {
    List<String> labels0 = new ArrayList<>();
    List<String> labels1 = Arrays.asList("Java","技术书籍");
    List<String> labels2 = Arrays.asList("男士","冬装");
    List<String> labels3 = Arrays.asList("男士","潮流");
    products.add(new Product(1,"苹果笔记本","电脑",new BigDecimal(13559),"办公",labels0));
    products.add(new Product(2,"深入了解Java虚拟机","",new BigDecimal(89),"书籍",labels1));
    products.add(new Product(3,"戴尔笔记本","电脑",new BigDecimal(4559),"办公",labels0));
    products.add(new Product(4,"男士外套","",new BigDecimal(210),"服装",labels2));
    products.add(new Product(5,"四叶草手链","",new BigDecimal(1500),"首饰",labels0));
    products.add(new Product(6,"男士短裤","",new BigDecimal(59),"服装",labels2));
    products.add(new Product(7,"方便面","",new BigDecimal(50),"零食",labels0));
    products.add(new Product(8,"三只松鼠","",new BigDecimal(99),"零食",labels0));
    products.add(new Product(9,"百草味","",new BigDecimal(118),"零食",labels0));
    products.add(new Product(10,"男士板鞋","",new BigDecimal(269),"鞋子",labels3));
    products.add(new Product(11,"方便面","",new BigDecimal(50),"零食",labels0));
}
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 4.1 遍历

遍历Stream中的每一个元素

//遍历
products.stream().forEach(e->System.out.println(e.getName()));
products.stream().peek(e->System.out.println(e.getName()));
1
2
3

# 4.2 匹配

Stream 有两个find方法,三个 match 方法:

  • findFirst: Stream 的第一个,可配合filter等使用
  • findAny: Stream中任意一个,可配合filter等使用
  • allMatch:Stream 中全部元素符合传入的 predicate,返回 true
  • anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
  • noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
 //匹配查找
 Optional<Product> firstPro = products.stream().findFirst();
 //匹配任意一个
 Optional<Product> anyPro = products.stream().findAny();
 //匹配id>4
 boolean match = products.stream().anyMatch(p->p.getId()>4);
1
2
3
4
5
6

# 4.3 过滤

通过传入断言匹配每一个元素,返回符合匹配的新的Stream流,也是最常用的方法之一。

//过滤描述为空的产品
List<Product> productList = products.stream().filter(
    item -> StringUtils.hasLength(item.getDesc()))
    .collect(Collectors.toList());

//过滤取其中的一个
Optional<Product> pro = products.stream().filter(
    item -> StringUtils.hasLength(item.getDesc())).findAny();

//过滤取其中的第一个
pro = products.stream().filter(item-> StringUtils.hasLength(item.getDesc())).findFirst();
1
2
3
4
5
6
7
8
9
10
11

# 4.4 收集

//统计每一类产品的数量
Map<String,Long> catMap = products.stream().collect(
    Collectors.groupingBy(Product::getCategory, Collectors.counting()));
        
//找出商品中价格最大的
Optional<Product> maxPricePro = products.stream().collect(
    Collectors.maxBy(Comparator.comparing(Product::getPrice)));
       
Optional<Product> maxPrice = products.stream().max(
    Comparator.comparing(Product::getPrice));
        
//统计所有商品价格
double sumPricePro = products.stream().collect(
    Collectors.summarizingDouble(e -> e.getPrice().doubleValue())).getSum();

//转换为Map<分类,产品>
Map<String, Product> productCatMap = products.stream().collect(
    Collectors.toMap(Product::getCategory, Function.identity(), (o, n) -> n));

//转换为Set
Set<Product> proSet = products.stream().collect(Collectors.toSet());

//根据某个属性去重
products.stream().collect(Collectors.collectingAndThen(
                Collectors.toCollection(()->new TreeSet<>(
                    Comparator.comparing(n->n.getName()))),ArrayList::new));
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

# 4.5 聚合

求最大值、最小值、计数,min 和 max 的功能也可以通过对 Stream 元素先排序,再 findFirst 来实现

//找出商品中价格最大的
Optional<Product> maxPrice = products.stream().max(
    Comparator.comparing(Product::getPrice));

//找出商品中价格最小的
Optional<Product> minPrice = products.stream().min(
    Comparator.comparing(Product::getPrice));

long proSize = products.stream().count();

//价格最高的商品
maxPricePro = products.stream().reduce(
    (p1, p2) -> p1.getPrice().compareTo(p2.getPrice())>0?p1:p2);
1
2
3
4
5
6
7
8
9
10
11
12
13

# 4.6 映射

映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map和flatMap:

  • map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
 //获取所有分类
 Set<String> cats = products.stream().map(
 	p -> p.getCategory()).collect(Collectors.toSet());

 //获取所有标签
 Set<String> labels = products.stream().map(
	 p -> p.getLabels()).flatMap(Collection::stream).collect(Collectors.toSet());
1
2
3
4
5
6
7

# 4.7 排序

这个方法的主要作用是对流元素按一定规则进行排序,它比数组的排序更强之处在于你可以首先对 Stream 进行各类 map、filter、limit、skip 甚至 distinct 来减少元素数量后,再排序,这能帮助程序明显缩短执行时间。

//按产品id排序
List<Product> orderAscList = products.stream().sorted(
    Comparator.comparing(Product::getId))
    .collect(Collectors.toList());

//按产品id降序
List<Product> orderDescList = products.stream().sorted(
    Comparator.comparing(Product::getId, Comparator.reverseOrder()))
    .collect(Collectors.toList());

 //前6条按产品id降序
orderDescList = products.stream().limit(6).sorted(
    Comparator.comparing(Product::getId, Comparator.reverseOrder()))
     .collect(Collectors.toList());
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 总结

本文主要讲了Java中的Stream流的功能介绍,API分类以及常用的操作,包含遍历、匹配、过滤、收集、聚合、映射、排序等代码示例。适当的使用Stream流可以让我们代码更加高效简洁。

最后更新时间: 2022/12/25, 22:09:58
Jdk8-19新特性
一文详解ThreadLocal是什么

← Jdk8-19新特性 一文详解ThreadLocal是什么→

最近更新
01
SuperBuilder
12-29
02
30岁我该怎么办
12-29
03
关于存钱
12-29
更多文章>
Theme by Vdoing | Copyright © 2021-2025 Mr.Fire | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式