实战:集合与流操作对比

分别使用集合操作及Stream流操作,完成对实际应用场景中的数据处理。直观感受流操作带来的便捷性。

购物车案例

沿用Java之函数编程中的购物车的案例。

需求条件:

  1. 想看看购物车中都有什么商品
  2. 图书类商品都给买
  3. 其余的商品中买两件最贵的
  4. 只需要两件商品的名称和总价

传统方式处理业务逻辑

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
47
48
49
/**
* 以原始集合操作实现需求
*/
public static void oldCartHandle() {
List<Sku> cartSKuList = CartService.getCartSKuList();
//1. 打印所有商品
for (Sku sku : cartSKuList) {
System.out.println(JSONUtil.parse(sku));
}
//2. 图书类过滤
List<Sku> notBooksSkuList = new ArrayList<>();
for (Sku sku : cartSKuList) {
if (!SkuCategoryEnum.BOOKS.equals(sku.getSkuCategory())) {
// 不是图书商品类的集合
notBooksSkuList.add(sku);
}
}
//3. 对总价格进行排序
notBooksSkuList.sort(new Comparator<Sku>() {
@Override
public int compare(Sku sku1, Sku sku2) {
if (sku1.getTotalPrice() > sku2.getTotalPrice()) {
return -1;
} else if (sku1.getTotalPrice() < sku2.getTotalPrice()) {
return 1;
} else {
return 0;
}
}
});
//4. 查找两件最贵的商品
List<Sku> topTwoSkuList = new ArrayList<>();
for (int i = 0; i < 2; i++) {
topTwoSkuList.add(notBooksSkuList.get(i));
}
//5. 求两件商品的总价
Double money = 0.00;
for (Sku sku : topTwoSkuList) {
money += sku.getTotalPrice();
}
//6. 获取两件商品的名称
List<String> resultSkuNameList = new ArrayList<>();
for (Sku sku : topTwoSkuList) {
resultSkuNameList.add(sku.getSkuName());
}
// 打印输出结果
System.out.println(JSONUtil.parse(resultSkuNameList));
System.out.println("商品总价:" + money);
}

Lambda与Stream处理业务逻辑

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
/**
* 以Stream流的方式实现需求
*/
public static void newCartHandle() {
// 原子引用类,多线程计数,线程安全
AtomicReference<Double> money = new AtomicReference<>(0.0);
List<String> resultSkuNameList = CartService.getCartSKuList()
.stream()
//1. 打印商品信息
.peek(sku -> System.out.println(JSONUtil.parse(sku)))
//2. 过滤掉所有图书类商品
.filter(sku -> !SkuCategoryEnum.BOOKS.equals(sku.getSkuCategory()))
//3. 根据总价进行排序
.sorted(Comparator.comparing(Sku::getTotalPrice).reversed())
//4. 查找两件最贵的商品
.limit(2)
//5. 求两件商品的总价
.peek(sku -> money.set(money.get() + sku.getTotalPrice()))
//6. 获取两件商品的名称
.map(Sku::getSkuName)
// 结果收集
.collect(Collectors.toList());
// 打印输出结果
System.out.println(JSONUtil.parse(resultSkuNameList));
System.out.println("商品总价:" + money.get());
}

Stream流

流是什么?

  • JDK1.8引入的新成员,以声明式方式处理集合数据
  • 将基础操作链接起来,完成复杂的数据处理流水线
  • 提供透明的并行处理

流的简介

从支持数据处理操作生成的元素序列。 – Java8实战

  • 元素序列:在java.util.stream.Stream当中定义了一个新的接口,可以访问特定元素类型的一组有序值。当中有stream方法,可以返回一个流。流的目的在于表达计算
  • :流会使用一个包含数据的源,比如集合,数组,输入输出的资源等。从有序集合生成流时会保留原有的顺序。由列表生成的流,其元素顺序与列表一致。
  • 数据处理的操作:流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中 的常用操作。流可以顺序执行,也可以并行执行。并行执行用parallStream就行。数据处理的操作就是 一个中间操作链,形成一条流的流水线。这些操作会返回一个流,但是如果没有终端操作,这些操作并不会执行。

流与集合的区别

  • 时间(流)与空间(集合)
    • 流面向于计算
    • 集合面向于存储
  • 只能遍历一次
    • 流只能遍历一次
    • 集合可以遍历多次
  • 外部迭代与内部迭代
    • 流是内部迭代
    • 集合时外部迭代

流的组成

image-20201028115525985

流操作分类

image-20201028115858508

流的使用

image-20201028130214238

  • 短路:执行时遇到满足条件则直接返回,不会继续执行
  • 非短路:执行时遇到满足条件则不会直接返回,会继续执行完成
  • 有状态:有状态就是有数据存储功能,线程不安全
  • 无状态:无状态就是一次操作,不能保存数据,线程安全