书接上文,咱们在第一天中学习了Lambda表达式、方法引用、函数式接口等诸多JDK8的新特性,今天呢,让我们在这些新特性的基础上学习强大的Steam流操作。
首先呢,我们还是延用之前的购物车的例子,分别使用原始的集合处理方式和Stream流处理方式,这两种数据处理方式来完成这一场景的数据处理,进而直观的感受流操作给我们带来的便捷性。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
* 对比:原始集合操作与Stream集合操作
*/
public class StreamVs {
/**
* 1 想看看购物车中都有什么商品
* 2 图书类商品都给买
* 3 其余的商品中买两件最贵的
* 4 只需要两件商品的名称和总价
*/
/**
* 以原始集合操作实现需求
*/
@Test
public void oldCartHandle() {
List<Sku> cartSkuList = CartService.getCartSkuList();
/**
* 1 打印所有商品
*/
for (Sku sku: cartSkuList) {
System.out.println(JSON.toJSONString(sku, true));
}
/**
* 2 图书类过滤掉
*/
List<Sku> notBooksSkuList = new ArrayList<Sku>();
for (Sku sku: cartSkuList) {
if (!SkuCategoryEnum.BOOKS.equals(sku.getSkuCategory())) {
notBooksSkuList.add(sku);
}
}
/**
* 排序
*/
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;
}
}
});
/**
* TOP2
*/
List<Sku> top2SkuList = new ArrayList<Sku>();
for (int i = 0; i < 2; i++) {
top2SkuList.add(notBooksSkuList.get(i));
}
/**
* 4 求两件商品的总价
*/
Double money = 0.0;
for (Sku sku: top2SkuList) {
// money = money + sku.getTotalPrice();
money += sku.getTotalPrice();
}
/**
* 获取两件商品的名称
*/
List<String> resultSkuNameList = new ArrayList<String>();
for (Sku sku: top2SkuList) {
resultSkuNameList.add(sku.getSkuName());
}
/**
* 打印输入结果
*/
System.out.println(
JSON.toJSONString(resultSkuNameList, true));
System.out.println("商品总价:" + money);
}
/**
* 以Stream流方式实现需求
*/
@Test
public void newCartHandle() {
AtomicReference<Double> money =
new AtomicReference<>(Double.valueOf(0.0));
List<String> resultSkuNameList =
CartService.getCartSkuList()
.stream()
/**
* 1 打印商品信息
*/
.peek(sku -> System.out.println(
JSON.toJSONString(sku, true)))
/**
* 2 过滤掉所有图书类商品
*/
.filter(sku -> !SkuCategoryEnum.BOOKS.equals(
sku.getSkuCategory()))
/**
* 排序
*/
.sorted(Comparator.
comparing(Sku::getTotalPrice).reversed())
/**
* TOP2
*/
.limit(2)
/**
* 累加商品总金额
*/
.peek(sku -> money.set(money.get() + sku.getTotalPrice()))
/**
* 获取商品名称
*/
.map(sku -> sku.getSkuName())
/**
* 收集结果
*/
.collect(Collectors.toList());
/**
* 打印输入结果
*/
System.out.println(
JSON.toJSONString(resultSkuNameList, true));
System.out.println("商品总价:" + money.get());
}
}
上边就是分别使用传统集合操作和Stream流处理集合的案例。这些就是咱们接下来要学习的重点。使用Stream以及它下面对流的操作,来帮助我们对集合进行一些处理。
通过之前的案例,我们可以明显的感觉到使用Stream流对集合的操作能为我们的开发带来那些便利性,那从现在开始我们来重点介绍一下Stream各方面的知识。
首先,流是什么?流呢它不是我们之前见到的IO流这种流操作,它是JDK1.8引入的新成员,以声明的方式处理集合数据,所谓声明式呢,其实是离不开Lambda表达式的。
第二呢,它是将基础的操作链接起来,完成复杂的数据处理的流水线。
第三呢,流操作为我们提供了一个透明的并行处理能力。
咱们依次来解释一下这三个划红线的词。
第一呢,就是什么是元素序列?与集合一样,流呢也提供一个接口,可以访问特定元素的一组有序值,这组有序值就是元素序列。
那什么是源呢?与现实中的水流类似,源是为流提供数据的源头,比如说集合、数组、输入输出的资源,都可以称之为源。
那什么是数据处理操作呢?流的数据处理操作支持类似数据库的操作,比如上面的map、sort、limit以及一些函数式编程中的常用操作。
下面我们来说一说流和集合的区别,首先是时间和空间上的区别。在这里可以举个例子,集合就相当于我们之前看的DVD光碟,你只有把这个光碟放到DVD机里才能观看整部电影;流呢,相当于现在的融媒体播放,你不用获取到一部电影的所有数据帧,只需要获取到当前正在观看的一定时间范围内的数据帧就可以。从这个角度来看,集合更像是空间上的元素的存储,流更像是时间维度上的数据的生成。所以一般对流和集合的定位是集合面向的是存储,而流面向的是计算。再举一个简单的例子,比如说让你构建一个质数集合,理论上说是构建不出来的,因为质数是无限大的,不可穷举;但是如果让你构建一个质数流,那是可以做到的,你只需要在使用到一个质数的时候实时地计算出这个质数就可以了。从这个角度上来说它们还是时间与空间上的区别。
那第二个不同是集合可以遍历多次,但是流呢因为是空间的概念,所以它只能遍历一次,再次遍历就会出现异常。
第三呢,是集合需要做外部迭代,类似于一开始咱们的那个例子,咱们需要对集合不停的做foreach迭代。那流呢,它是内部迭代,咱们只需要定义需要对流中的每一个元素做什么操作,然后呢,流就会帮我们做内部的迭代,去处理这些操作。这是流与集合的一些区别。
然后呢,再看一看流的组成, 流分三部分组成,第一部分是 数据源。第二部分是 中间操作。第三部分是 终端操作。中间操作主要是我们做自己的业务处理。就像上面最开始的例子一样,咱们对它进行过滤、排序、解析。然后呢,终端操作是对流的收集。咱们可以将数据源产生的数据在这里做一个收集,形成咱们的数据结构。
我们对流的操作可以大致分一下类,我们可以看一下上面的思维导图。上面说的流的组成有一个 中间操作 和一个 终端操作。而流的操作也是按 中间操作 和 终端操作 分的两大类。
在中间操作中呢,有 无状态操作 和 有状态操作 两大类。那怎么理解这个呢?咱们可以想像一下,有过SQL经验的肯定会知道,比如说这个distinct去重它一定是在所有数据的基础上才能把重复的去掉;在比如说sorted排序也是肯定在所有的数据基础上进行一个排序。那这种呢我们就称之为有状态的操作。
而还有一些呢,比如说filter过滤,过滤很简单,就是当前这个数据它的某些字段是否符合咱们的要求,那就是过滤,它并不需要建立在所有数据的基础上来实现,它只需要对单个的数据进行实现,进行判断就好了。这个称为无状态的操作。
再来看终端操作,分为两类,一种是 非短路操作,另一种是 短路操作。
那什么是 非短路操作?就是数据集有多个数据,每个数据都要执行一遍forEach,这个就是非短路操作。
那短路操作就是比如说你要找到第一个元素,那不管我这个数据集有多少,只要找到第一个元素,后面的元素就都不会执行了。那这就是短路操作。
这里对流所支持的所有操作进行了一些罗列, 并且已经分好类了。各位小伙伴在使用流的时候可以参考这个分类,选择合适的方式来使用流的各种操作。之后的实战案例也将演示流的每种使用方式。
下面通过实际应用场景,介绍流的使用。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
}
首先使用list创建一个stream流,接着调用filter,因为它是一个中间操作,流的构成中说了,最后还要有一个终端操作,咱们的终端操作先统一使用
item ->
System.out.println(
JSON.toJSONString(
item, true))
将结果打印出来,不管它是什么。那咱们接下来重点看一下filter如何使用。
咱们点进去看到,filter接收一个Predicate函数式接口, 那咱们在学Lambda表达式时也说过了,Predicate呢它里面只有一个test,只要你把参数传进来,然后它根据业务规则来判断,返回一个True或者False。filter也比较简单,需要传入的是一个集合元素,那就是sku,那咱们根据某些规则来判断sku是不是符合这个断言。如果符合filter就不会过滤这个元素,如果不符合filter就会将这个元素过滤掉。咱们先判断sku是不是书籍类?如果等于书籍类,就将它保留,如果不等于就将它过滤。
以上是filter的一个简单的使用。
接下来咱们看map。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
}
map方法呢,看看它的传入参数是什么?
是一个Function的函数接口。
那Function做什么的呢?Function是将一个T型的输入参数转换为一个R型的输出参数。
那很明显,map的作用就是将一个集合元素转换为另一种形式的集合元素。咱们就可以将sku对象转换为String对象。
接下来咱们看一个与map类似,但又有一点差异的方法,叫做flatMap。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
}
翻译过来叫做 扁平化Map,这个操作是做什么用的呢?咱们看看它接收哪些参数?
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
它还是接收一个Function函数式接口,但是它返回的R咱们注意观察,这个R是一个Stream类型的流。那说明flatMap接收一个元素,返回一个新的流,这个流将会与其他元素产生的流进行一个合并,最后统一的由forEach来进行输出。咱们这里将商品的名称进行切分,返回一个切分后的字符流。
下面咱们来看peek。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
}
这个操作呢其实与forEach操作是一样的,但是它与forEach操作本质的区别是它是一个中间操作,而forEach是一个终端操作,forEach操作之后这个流就不可用了。peek操作之后的流是可以被后续环节继续使用的,咱们来看看它的接收参数。
Stream<T> peek(Consumer<? super T> action);
是一个Consumer的函数式接口,Consumer里面就有一个accept,对数据进行一些操作,它没有返回值。它只是单纯的数据操作。
import java.util.Objects;
/**
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #accept(Object)}.
*
* @param <T> the type of the input to the operation
*
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
那咱们对sku的名称进行打印吧。
这里可以看到一个现象,就是forEach和peek是交替执行的。,并不是peek完全执行完,forEach才执行,而是一条数据先经过peek,然后再经过forEach,那这也是流执行的一个特点。流是惰性执行,只有遇到forEach这种终端操作,流的元素才会由上到下依次执行,恰好peek这种中间操作呢又是无状态的中间操作,也是就说先执行谁并没有很大的区别,对结果不会有影响,所以
它就会先执行peek中间操作,紧跟着执行forEach终端操作。
下面我们要讲的是有状态的中间操作,此时结果就会不一样。
咱们先来看一个sorted操作。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
}
sorted操作呢,无参的是按照默认的自然排序策略来排。那咱们加一个有参数的。根据sku的总价来排。
咱们先看排序之后的效果是总价是按照从小到大的顺序排列的。
所以说sorted操作起到了它应有的作用。咱们这里呢再加一个peek操作。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
}
peek操作还是在打印这个商品名称。这里咱们注意观察,通过中间添加sorted操作之后,peek操作还会和forEach操作一起打印吗?
可以看到很明显,这个商品名称的是peek操作来打印出来的。而后面的呢是forEach操作来打印出来的。它们已经有了一个很明显的先后顺序。所有数据在执行完peek操作之后才会执行forEach操作。这个原因就是咱们在中间加了一个有状态的sorted操作,所有经过peek的数据都要先在sorted这里做一个汇总,由sorted进行统一排序之后再交由下一个环节进行处理。
这个就是有状态操作和无状态操作的区别,会对数据执行的先后有影响。
接下来我们来演示distinct去重。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* distinct使用:对流元素进行去重。有状态操作
*/
@Test
public void distinctTest() {
list.stream()
.map(sku -> sku.getSkuCategory())
// distinct
.distinct()
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
}
我们的目的是把sku的所有分类打印出来,然后将他们去重看看有几种分类。首先咱们通过map将sku转换成它的类别对象,紧接着通过类别对象对它去重,接着打印去重之后剩下的sku的分类。
可以看到咱们的购物车中的sku一共涉及到了四个分类。这四个分类都是经过去重之后留下的。这是distinct的一个用途。
接下来咱们继续看skip。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* distinct使用:对流元素进行去重。有状态操作
*/
@Test
public void distinctTest() {
list.stream()
.map(sku -> sku.getSkuCategory())
// distinct
.distinct()
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* skip使用:跳过前N条记录。有状态操作
*/
@Test
public void skipTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
// skip
.skip(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
}
skip的作用是跳过前几条元素,在跳过之前咱们先给它排个序,我们还是根据总价进行排序,接着咱们调用skip过滤掉前三条数据,然后咱们进行数据的打印。
可以看到咱们的数据是从149开始,前三条70多的已经被过滤掉了。
最后咱们来看一个有状态的操作limit。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* distinct使用:对流元素进行去重。有状态操作
*/
@Test
public void distinctTest() {
list.stream()
.map(sku -> sku.getSkuCategory())
// distinct
.distinct()
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* skip使用:跳过前N条记录。有状态操作
*/
@Test
public void skipTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
// skip
.skip(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* limit使用:截断前N条记录。有状态操作
*/
@Test
public void limitTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
.skip(2 * 3)
// limit
.limit(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
}
那有人会想了,skip是跳过某几条数据,limit又是限制了几条数据,那如果把它们两个结合起来做一个假的分页其实是可以实现的。比如说第三页,咱们以三条数据为一页吧,如果是第三页就是跳过skip(2 * 3),再取后三条数据limit(3)。这样就实现了一个假的分页。
接下来我们对终端操作进行演示。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* distinct使用:对流元素进行去重。有状态操作
*/
@Test
public void distinctTest() {
list.stream()
.map(sku -> sku.getSkuCategory())
// distinct
.distinct()
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* skip使用:跳过前N条记录。有状态操作
*/
@Test
public void skipTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
// skip
.skip(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* limit使用:截断前N条记录。有状态操作
*/
@Test
public void limitTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
.skip(2 * 3)
// limit
.limit(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* allMatch使用:终端操作,短路操作。所有元素匹配,返回true
*/
@Test
public void allMatchTest() {
boolean match = list.stream()
// allMatch
.allMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
}
allMatch用来检测所有的元素是否都满足断言?如果都满足返回True,如果有一个不满足返回false。因为是终端操作,我们可以直接使用,也没有中间操作,我们来判断总价是否大于100,这里的返回类型是Boolean类型的。
刚才我们说了,这个allMatch是一个终端操作,并且是一个短路操作,当它检测到第一个不满足这个条件的,它就会返回这个Boolean返回值,那怎么来看呢,咱们使用peek打印一下。
/**
* allMatch使用:终端操作,短路操作。所有元素匹配,返回true
*/
@Test
public void allMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// allMatch
.allMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
对当前的skuName进行打印,如果peek将所有的集合元素都打印了,那说明所有的元素都经过了allMatch的匹配。那跟我们之前的猜想就不符了,如果只打印了部分sku,那就证明部分sku,当有一个sku不通过allMatch的检测的时候,那后续的sku就不会执行。
可以看到我们打印的sku件数不够,起码图书我们应该打印出四种,而现在只打印出一种。说明allMatch操作是一个短路操作,它会依次消费流中的元素,如果有一个不匹配,将不会消费后面的元素,会直接返回一个结果。
跟它相似的还有anyMatch。它的意思是只要有元素满足它的断言就会返回True,否则的话就会返回False。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* distinct使用:对流元素进行去重。有状态操作
*/
@Test
public void distinctTest() {
list.stream()
.map(sku -> sku.getSkuCategory())
// distinct
.distinct()
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* skip使用:跳过前N条记录。有状态操作
*/
@Test
public void skipTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
// skip
.skip(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* limit使用:截断前N条记录。有状态操作
*/
@Test
public void limitTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
.skip(2 * 3)
// limit
.limit(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* allMatch使用:终端操作,短路操作。所有元素匹配,返回true
*/
@Test
public void allMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// allMatch
.allMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* anyMatch使用:任何元素匹配,返回true
*/
@Test
public void anyMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// anyMatch
.anyMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
}
最后一个是noneMatch。
package com.imooc.lvdapiaoliang.stream;
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* distinct使用:对流元素进行去重。有状态操作
*/
@Test
public void distinctTest() {
list.stream()
.map(sku -> sku.getSkuCategory())
// distinct
.distinct()
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* skip使用:跳过前N条记录。有状态操作
*/
@Test
public void skipTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
// skip
.skip(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* limit使用:截断前N条记录。有状态操作
*/
@Test
public void limitTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
.skip(2 * 3)
// limit
.limit(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* allMatch使用:终端操作,短路操作。所有元素匹配,返回true
*/
@Test
public void allMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// allMatch
.allMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* anyMatch使用:任何元素匹配,返回true
*/
@Test
public void anyMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// anyMatch
.anyMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* noneMatch使用:任何元素都不匹配,返回true
*/
@Test
public void noneMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// noneMatch
.noneMatch(sku -> sku.getTotalPrice() > 10_000);
System.out.println(match);
}
}
接下来我们来介绍findFirst。它返回的是一个Optional。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* distinct使用:对流元素进行去重。有状态操作
*/
@Test
public void distinctTest() {
list.stream()
.map(sku -> sku.getSkuCategory())
// distinct
.distinct()
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* skip使用:跳过前N条记录。有状态操作
*/
@Test
public void skipTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
// skip
.skip(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* limit使用:截断前N条记录。有状态操作
*/
@Test
public void limitTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
.skip(2 * 3)
// limit
.limit(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* allMatch使用:终端操作,短路操作。所有元素匹配,返回true
*/
@Test
public void allMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// allMatch
.allMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* anyMatch使用:任何元素匹配,返回true
*/
@Test
public void anyMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// anyMatch
.anyMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* noneMatch使用:任何元素都不匹配,返回true
*/
@Test
public void noneMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// noneMatch
.noneMatch(sku -> sku.getTotalPrice() > 10_000);
System.out.println(match);
}
/**
* 找到第一个
*/
@Test
public void findFirstTest() {
Optional<Sku> optional = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// findFirst
.findFirst();
System.out.println(
JSON.toJSONString(optional.get(), true));
}
}
与它相似的还有findAny。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* distinct使用:对流元素进行去重。有状态操作
*/
@Test
public void distinctTest() {
list.stream()
.map(sku -> sku.getSkuCategory())
// distinct
.distinct()
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* skip使用:跳过前N条记录。有状态操作
*/
@Test
public void skipTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
// skip
.skip(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* limit使用:截断前N条记录。有状态操作
*/
@Test
public void limitTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
.skip(2 * 3)
// limit
.limit(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* allMatch使用:终端操作,短路操作。所有元素匹配,返回true
*/
@Test
public void allMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// allMatch
.allMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* anyMatch使用:任何元素匹配,返回true
*/
@Test
public void anyMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// anyMatch
.anyMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* noneMatch使用:任何元素都不匹配,返回true
*/
@Test
public void noneMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// noneMatch
.noneMatch(sku -> sku.getTotalPrice() > 10_000);
System.out.println(match);
}
/**
* 找到第一个
*/
@Test
public void findFirstTest() {
Optional<Sku> optional = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// findFirst
.findFirst();
System.out.println(
JSON.toJSONString(optional.get(), true));
}
/**
* 找任意一个
*/
@Test
public void findAnyTest() {
Optional<Sku> optional = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// findAny
.findAny();
System.out.println(
JSON.toJSONString(optional.get(), true));
}
}
那在使用findFirst和findAny有什么区别呢?其实在并行上是有区别的,因为找到第一个元素在并行上的限制会更多一点;找到任意一个在并行上的限制会少。所以总体上来说,findAny在并行上的速度会比findFirst要快。但是findAny有一个缺点是它可能会随机匹配到一个元素。所以findAny返回的元素可能会不一致。但是如果两个流都是串行操作,那个这两个没什么区别。
接下来我们来看几个非短路的终端操作。
首先是max,求一个集合中某个属性的最大值。
package com.imooc.lvdapiaoliang.stream;
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* distinct使用:对流元素进行去重。有状态操作
*/
@Test
public void distinctTest() {
list.stream()
.map(sku -> sku.getSkuCategory())
// distinct
.distinct()
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* skip使用:跳过前N条记录。有状态操作
*/
@Test
public void skipTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
// skip
.skip(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* limit使用:截断前N条记录。有状态操作
*/
@Test
public void limitTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
.skip(2 * 3)
// limit
.limit(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* allMatch使用:终端操作,短路操作。所有元素匹配,返回true
*/
@Test
public void allMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// allMatch
.allMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* anyMatch使用:任何元素匹配,返回true
*/
@Test
public void anyMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// anyMatch
.anyMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* noneMatch使用:任何元素都不匹配,返回true
*/
@Test
public void noneMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// noneMatch
.noneMatch(sku -> sku.getTotalPrice() > 10_000);
System.out.println(match);
}
/**
* 找到第一个
*/
@Test
public void findFirstTest() {
Optional<Sku> optional = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// findFirst
.findFirst();
System.out.println(
JSON.toJSONString(optional.get(), true));
}
/**
* 找任意一个
*/
@Test
public void findAnyTest() {
Optional<Sku> optional = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// findAny
.findAny();
System.out.println(
JSON.toJSONString(optional.get(), true));
}
/**
* max使用:
*/
@Test
public void maxTest() {
OptionalDouble optionalDouble = list.stream()
// 获取总价
.mapToDouble(Sku::getTotalPrice)
.max();
System.out.println(optionalDouble.getAsDouble());
}
}
首先我们来解释一下mapToDouble是什么意思?我们知道map是将一个元素映射到另一个元素,那mapToDouble是将一个元素映射成Double类型的元素。
下面我们来看min。
package com.imooc.lvdapiaoliang.stream;
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* distinct使用:对流元素进行去重。有状态操作
*/
@Test
public void distinctTest() {
list.stream()
.map(sku -> sku.getSkuCategory())
// distinct
.distinct()
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* skip使用:跳过前N条记录。有状态操作
*/
@Test
public void skipTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
// skip
.skip(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* limit使用:截断前N条记录。有状态操作
*/
@Test
public void limitTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
.skip(2 * 3)
// limit
.limit(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* allMatch使用:终端操作,短路操作。所有元素匹配,返回true
*/
@Test
public void allMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// allMatch
.allMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* anyMatch使用:任何元素匹配,返回true
*/
@Test
public void anyMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// anyMatch
.anyMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* noneMatch使用:任何元素都不匹配,返回true
*/
@Test
public void noneMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// noneMatch
.noneMatch(sku -> sku.getTotalPrice() > 10_000);
System.out.println(match);
}
/**
* 找到第一个
*/
@Test
public void findFirstTest() {
Optional<Sku> optional = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// findFirst
.findFirst();
System.out.println(
JSON.toJSONString(optional.get(), true));
}
/**
* 找任意一个
*/
@Test
public void findAnyTest() {
Optional<Sku> optional = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// findAny
.findAny();
System.out.println(
JSON.toJSONString(optional.get(), true));
}
/**
* max使用:
*/
@Test
public void maxTest() {
OptionalDouble optionalDouble = list.stream()
// 获取总价
.mapToDouble(Sku::getTotalPrice)
.max();
System.out.println(optionalDouble.getAsDouble());
}
/**
* min使用
*/
@Test
public void minTest() {
OptionalDouble optionalDouble = list.stream()
// 获取总价
.mapToDouble(Sku::getTotalPrice)
.min();
System.out.println(optionalDouble.getAsDouble());
}
}
原理也是一样的,换成min就可以了。
下面我们来看cout。count是获取元素总数。
package com.imooc.lvdapiaoliang.stream;
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import com.imooc.lvdapiaoliang.lambda.cart.SkuCategoryEnum;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
/**
* 演示流的各种操作
*/
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
/**
* filter使用:过滤掉不符合断言判断的数据
*/
@Test
public void filterTest() {
list.stream()
// filter
.filter(sku ->
SkuCategoryEnum.BOOKS
.equals(sku.getSkuCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* map使用:将一个元素转换成另一个元素
*/
@Test
public void mapTest() {
list.stream()
// map
.map(sku -> sku.getSkuName())
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* flatMap使用:将一个对象转换成流
*/
@Test
public void flatMapTest() {
list.stream()
// flatMap
.flatMap(sku -> Arrays.stream(
sku.getSkuName().split("")))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* peek使用:对流中元素进行遍历操作,与forEach类似,但不会销毁流元素
*/
@Test
public void peek() {
list.stream()
// peek
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
//sort
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* distinct使用:对流元素进行去重。有状态操作
*/
@Test
public void distinctTest() {
list.stream()
.map(sku -> sku.getSkuCategory())
// distinct
.distinct()
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* skip使用:跳过前N条记录。有状态操作
*/
@Test
public void skipTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
// skip
.skip(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* limit使用:截断前N条记录。有状态操作
*/
@Test
public void limitTest() {
list.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
.skip(2 * 3)
// limit
.limit(3)
.forEach(item ->
System.out.println(
JSON.toJSONString(
item, true)));
}
/**
* allMatch使用:终端操作,短路操作。所有元素匹配,返回true
*/
@Test
public void allMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// allMatch
.allMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* anyMatch使用:任何元素匹配,返回true
*/
@Test
public void anyMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// anyMatch
.anyMatch(sku -> sku.getTotalPrice() > 100);
System.out.println(match);
}
/**
* noneMatch使用:任何元素都不匹配,返回true
*/
@Test
public void noneMatchTest() {
boolean match = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// noneMatch
.noneMatch(sku -> sku.getTotalPrice() > 10_000);
System.out.println(match);
}
/**
* 找到第一个
*/
@Test
public void findFirstTest() {
Optional<Sku> optional = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// findFirst
.findFirst();
System.out.println(
JSON.toJSONString(optional.get(), true));
}
/**
* 找任意一个
*/
@Test
public void findAnyTest() {
Optional<Sku> optional = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
// findAny
.findAny();
System.out.println(
JSON.toJSONString(optional.get(), true));
}
/**
* max使用:
*/
@Test
public void maxTest() {
OptionalDouble optionalDouble = list.stream()
// 获取总价
.mapToDouble(Sku::getTotalPrice)
.max();
System.out.println(optionalDouble.getAsDouble());
}
/**
* min使用
*/
@Test
public void minTest() {
OptionalDouble optionalDouble = list.stream()
// 获取总价
.mapToDouble(Sku::getTotalPrice)
.min();
System.out.println(optionalDouble.getAsDouble());
}
/**
* count使用
*/
@Test
public void countTest() {
long count = list.stream()
.count();
System.out.println(count);
}
}
它会获取到Stream流中的所有元素的个数并且返回。
接下来我们来演示流的四种构建形式。
我们先创建一个StreamConstructor类,来演示流的四种构建形式。
首先我们来看由值直接构建流。
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* 流的四种构建形式
*/
public class StreamConstructor {
/**
* 由数值直接构建流
*/
@Test
public void streamFromValue() {
Stream stream = Stream.of(1, 2, 3, 4, 5);
stream.forEach(System.out::println);
}
}
Stream有一个静态的of方法,它可以接收任意数量的参数,通过显示值来创建流。
第二种方式是通过数组构建流。
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* 流的四种构建形式
*/
public class StreamConstructor {
/**
* 由数值直接构建流
*/
@Test
public void streamFromValue() {
Stream stream = Stream.of(1, 2, 3, 4, 5);
stream.forEach(System.out::println);
}
/**
* 通过数组构建流
*/
@Test
public void streamFromArray() {
int[] numbers = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(numbers);
stream.forEach(System.out::println);
}
}
接着我们来看通过文件来构建流,Java的NIO呢,为我们提供了一个Files工具类,它里面有很多的静态方法都可以返回一个流。比如说lines方法,它会返回一个由指定文件中的各行构成的字符串流。
package com.imooc.lvdapiaoliang.stream;
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* 流的四种构建形式
*/
public class StreamConstructor {
/**
* 由数值直接构建流
*/
@Test
public void streamFromValue() {
Stream stream = Stream.of(1, 2, 3, 4, 5);
stream.forEach(System.out::println);
}
/**
* 通过数组构建流
*/
@Test
public void streamFromArray() {
int[] numbers = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(numbers);
stream.forEach(System.out::println);
}
/**
* 通过文件生成流
* @throws IOException
*/
@Test
public void streamFromFile() throws IOException {
// TODO 此处替换为本地文件的地址全路径
String filePath = "";
Stream<String> stream = Files.lines(
Paths.get(filePath));
stream.forEach(System.out::println);
}
}
接下来我们来看通过函数来生成一个流。
Stream为我们提供了两种根据函数生成流的方法,一种是iterate,利用迭代的方式生成,比如说咱们要生成一个正偶数的流。
package com.imooc.lvdapiaoliang.stream;
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* 流的四种构建形式
*/
public class StreamConstructor {
/**
* 由数值直接构建流
*/
@Test
public void streamFromValue() {
Stream stream = Stream.of(1, 2, 3, 4, 5);
stream.forEach(System.out::println);
}
/**
* 通过数组构建流
*/
@Test
public void streamFromArray() {
int[] numbers = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(numbers);
stream.forEach(System.out::println);
}
/**
* 通过文件生成流
* @throws IOException
*/
@Test
public void streamFromFile() throws IOException {
// TODO 此处替换为本地文件的地址全路径
String filePath = "";
Stream<String> stream = Files.lines(
Paths.get(filePath));
stream.forEach(System.out::println);
}
/**
* 通过函数生成流(无限流)
*/
@Test
public void streamFromFunction() {
Stream stream = Stream.iterate(0, n -> n + 2);
stream.forEach(System.out::println);
}
}
它的生成方式就是在原来的值基础上加2生成新的流,而最开始的值是0,也就是从0开始的,2,4,6......一直生成下去。如果我们想创建一个无限的集合那是不可能的,但是我们可以通过流来创建一个无限的流。这就是集合与流的不同。
咱们看完这种生成方式,咱们看下一种生成方式,Stream还为我们提供了一种叫generate的方法,它是通过一个生成器来生成流。跟迭代不同的是后一个流不会基于上一个流生成,而是随机的生成。
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* 流的四种构建形式
*/
public class StreamConstructor {
/**
* 由数值直接构建流
*/
@Test
public void streamFromValue() {
Stream stream = Stream.of(1, 2, 3, 4, 5);
stream.forEach(System.out::println);
}
/**
* 通过数组构建流
*/
@Test
public void streamFromArray() {
int[] numbers = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(numbers);
stream.forEach(System.out::println);
}
/**
* 通过文件生成流
* @throws IOException
*/
@Test
public void streamFromFile() throws IOException {
// TODO 此处替换为本地文件的地址全路径
String filePath = "";
Stream<String> stream = Files.lines(
Paths.get(filePath));
stream.forEach(System.out::println);
}
/**
* 通过函数生成流(无限流)
*/
@Test
public void streamFromFunction() {
// Stream stream = Stream.iterate(0, n -> n + 2);
Stream stream = Stream.generate(Math::random);
stream.limit(100)
.forEach(System.out::println);
}
}
这次咱们给流做一个限制,只要前一百个,否则又无穷无尽的生成下去。
可以看到,生成了100个随机的Double类型的数值的流, 这就是通过函数生成流,流呢是一个无限流,所以需要通过limit来限制生成个数。
以上呢就是流的四种构建形式。
在前面我们讲解完了流的创建和各种操作,接下来我们来讲解流的收集。我们已经处理好的流如何将它收集起来?比如收集成一个List集合或者收集成一个Set集合呢?那这就要看咱们收集器的作用。
收集器的作用就是将流中的元素累积成一个结果,这个结果可以是数值,可以是集合,包括可以是个分组。
收集器作用于终端操作collect()上。
可能小伙伴有几个概念可能会混淆,就是collect/Collector/Collectors,那collect是作为一个终端操作出现的,它是流收集的最后一个步骤,是一个方法。那Collector是一个接口,collect这个方法需要接收一个实现了一个Collector接口的这么一个收集器才可以收集。那Collectors是一个工具类,它帮我们提前封装好了一些已经预制好的实现了Collector接口的收集器,我们可以直接拿过来用。这就是它们三个的关系。
刚才咱们谈到了Collectors这个工具类为我们预制了一些收集器的实现,都有哪些实现呢?主要分三种。
下面通过一个实战案例来演示 一下预定义的收集器的使用方式。
接下来我们来演示一下三种非常常见的预定义收集器的使用方式。
首先咱们先创建一个StreamCollector类。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 常见预定义收集器使用
*/
public class StreamCollector {
/**
* 集合收集器
*/
@Test
public void toList() {
List<Sku> list = CartService.getCartSkuList();
List<Sku> result = list.stream()
.filter(sku -> sku.getTotalPrice() > 100)
.collect(Collectors.toList());
System.out.println(
JSON.toJSONString(result, true));
}
}
首先是toList方法,直接将我们处理好的流收集成一个List集合,那细心的小伙伴可能在前面看到过这种用法,这里咱们简单的讲解一下,首先还是先获取咱们的流,然后咱们做一下过滤,将总价大于100的过滤出来,接着咱们调用collect,它接收一个Collector的接口实现类。
<R, A> R collect(Collector<? super T, A, R> collector);
那咱们可以调用Collectors.toList()方法,它帮我们预定义了一个Collector的接口实现类。就是将集合中的元素收集为一个List返回。它呢,返回一个List集合。
Connected to the target VM, address: '127.0.0.1:11936', transport: 'socket'
[
{
"skuCategory":"ELECTRONICS",
"skuId":654032,
"skuName":"无人机",
"skuPrice":4999.0,
"totalNum":1,
"totalPrice":4999.0
},
{
"skuCategory":"ELECTRONICS",
"skuId":642934,
"skuName":"VR一体机",
"skuPrice":2299.0,
"totalNum":1,
"totalPrice":2299.0
},
{
"skuCategory":"CLOTHING",
"skuId":645321,
"skuName":"纯色衬衫",
"skuPrice":409.0,
"totalNum":3,
"totalPrice":1227.0
},
{
"skuCategory":"CLOTHING",
"skuId":654327,
"skuName":"牛仔裤",
"skuPrice":528.0,
"totalNum":1,
"totalPrice":528.0
},
{
"skuCategory":"SPORTS",
"skuId":675489,
"skuName":"跑步机",
"skuPrice":2699.0,
"totalNum":1,
"totalPrice":2699.0
},
{
"skuCategory":"BOOKS",
"skuId":678678,
"skuName":"Java核心技术",
"skuPrice":149.0,
"totalNum":1,
"totalPrice":149.0
}
]
Disconnected from the target VM, address: '127.0.0.1:11936', transport: 'socket'
Process finished with exit code 0
可以看到返回的是一个集合类型。
接下来我们来看看分组,这个也比较常用。咱们现在要求:将商品根据类别进行分组。借助于Stream流咱们可以很简单的来实现这个功能。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 常见预定义收集器使用
*/
public class StreamCollector {
/**
* 集合收集器
*/
@Test
public void toList() {
List<Sku> list = CartService.getCartSkuList();
List<Sku> result = list.stream()
.filter(sku -> sku.getTotalPrice() > 100)
.collect(Collectors.toList());
System.out.println(
JSON.toJSONString(result, true));
}
/**
* 分组
*/
@Test
public void group() {
List<Sku> list = CartService.getCartSkuList();
// Map<分组条件,结果集合>
Map<Object, List<Sku>> group = list.stream()
.collect(
Collectors.groupingBy(
sku -> sku.getSkuCategory()));
System.out.println(
JSON.toJSONString(group, true));
}
}
首先咱们来创建Stream流,接着咱们调用collect收集器,Collectors为我们提供了一个groupingBy方法,它就是来做分组用的,groupingBy它接收一个Function,这个Function就是用来作为分组的标识,你需要用哪些属性来做分组。它会返回一个map,map的key就是你作为分组的条件,value呢是一个元素的集合。
Connected to the target VM, address: '127.0.0.1:11916', transport: 'socket'
{"SPORTS":[
{
"skuCategory":"SPORTS",
"skuId":675489,
"skuName":"跑步机",
"skuPrice":2699.0,
"totalNum":1,
"totalPrice":2699.0
}
],"CLOTHING":[
{
"skuCategory":"CLOTHING",
"skuId":645321,
"skuName":"纯色衬衫",
"skuPrice":409.0,
"totalNum":3,
"totalPrice":1227.0
},
{
"skuCategory":"CLOTHING",
"skuId":654327,
"skuName":"牛仔裤",
"skuPrice":528.0,
"totalNum":1,
"totalPrice":528.0
}
],"ELECTRONICS":[
{
"skuCategory":"ELECTRONICS",
"skuId":654032,
"skuName":"无人机",
"skuPrice":4999.0,
"totalNum":1,
"totalPrice":4999.0
},
{
"skuCategory":"ELECTRONICS",
"skuId":642934,
"skuName":"VR一体机",
"skuPrice":2299.0,
"totalNum":1,
"totalPrice":2299.0
}
],"BOOKS":[
{
"skuCategory":"BOOKS",
"skuId":644564,
"skuName":"Java编程思想",
"skuPrice":79.8,
"totalNum":1,
"totalPrice":79.8
},
{
"skuCategory":"BOOKS",
"skuId":678678,
"skuName":"Java核心技术",
"skuPrice":149.0,
"totalNum":1,
"totalPrice":149.0
},
{
"skuCategory":"BOOKS",
"skuId":697894,
"skuName":"算法",
"skuPrice":78.2,
"totalNum":1,
"totalPrice":78.2
},
{
"skuCategory":"BOOKS",
"skuId":696968,
"skuName":"TensorFlow进阶指南",
"skuPrice":85.1,
"totalNum":1,
"totalPrice":85.1
}
]
}
Disconnected from the target VM, address: '127.0.0.1:11916', transport: 'socket'
Process finished with exit code 0
那咱们再来看一看,还有一种是分区。分区是分组的一种特殊情况,它是由一个谓词作为分类的函数,也称它为分区函数,这个分区函数会返回一个Boolean值。这意味着将数据分成两组,一组是Boolean为True的,一组是Boolean为False的。那咱们来看一看演示。
import com.alibaba.fastjson.JSON;
import com.imooc.lvdapiaoliang.lambda.cart.CartService;
import com.imooc.lvdapiaoliang.lambda.cart.Sku;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 常见预定义收集器使用
*/
public class StreamCollector {
/**
* 集合收集器
*/
@Test
public void toList() {
List<Sku> list = CartService.getCartSkuList();
List<Sku> result = list.stream()
.filter(sku -> sku.getTotalPrice() > 100)
.collect(Collectors.toList());
System.out.println(
JSON.toJSONString(result, true));
}
/**
* 分组
*/
@Test
public void group() {
List<Sku> list = CartService.getCartSkuList();
// Map<分组条件,结果集合>
Map<Object, List<Sku>> group = list.stream()
.collect(
Collectors.groupingBy(
sku -> sku.getSkuCategory()));
System.out.println(
JSON.toJSONString(group, true));
}
/**
* 分区
*/
@Test
public void partition() {
List<Sku> list = CartService.getCartSkuList();
Map<Boolean, List<Sku>> partition = list.stream()
.collect(Collectors.partitioningBy(
sku -> sku.getTotalPrice() > 100));
System.out.println(
JSON.toJSONString(partition, true));
}
}
Connected to the target VM, address: '127.0.0.1:12122', transport: 'socket'
{false:[
{
"skuCategory":"BOOKS",
"skuId":644564,
"skuName":"Java编程思想",
"skuPrice":79.8,
"totalNum":1,
"totalPrice":79.8
},
{
"skuCategory":"BOOKS",
"skuId":697894,
"skuName":"算法",
"skuPrice":78.2,
"totalNum":1,
"totalPrice":78.2
},
{
"skuCategory":"BOOKS",
"skuId":696968,
"skuName":"TensorFlow进阶指南",
"skuPrice":85.1,
"totalNum":1,
"totalPrice":85.1
}
],true:[
{
"skuCategory":"ELECTRONICS",
"skuId":654032,
"skuName":"无人机",
"skuPrice":4999.0,
"totalNum":1,
"totalPrice":4999.0
},
{
"skuCategory":"ELECTRONICS",
"skuId":642934,
"skuName":"VR一体机",
"skuPrice":2299.0,
"totalNum":1,
"totalPrice":2299.0
},
{
"skuCategory":"CLOTHING",
"skuId":645321,
"skuName":"纯色衬衫",
"skuPrice":409.0,
"totalNum":3,
"totalPrice":1227.0
},
{
"skuCategory":"CLOTHING",
"skuId":654327,
"skuName":"牛仔裤",
"skuPrice":528.0,
"totalNum":1,
"totalPrice":528.0
},
{
"skuCategory":"SPORTS",
"skuId":675489,
"skuName":"跑步机",
"skuPrice":2699.0,
"totalNum":1,
"totalPrice":2699.0
},
{
"skuCategory":"BOOKS",
"skuId":678678,
"skuName":"Java核心技术",
"skuPrice":149.0,
"totalNum":1,
"totalPrice":149.0
}
]
}
Disconnected from the target VM, address: '127.0.0.1:12122', transport: 'socket'
Process finished with exit code 0
这里咱们根据总价是否大于100进行分区,还是调用咱们的collect,Collectors为我们提供了一个partitioningBy方法,它就是来做分区用的,partitioningBy它接收一个Predicate。
public static <T>
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
return partitioningBy(predicate, toList());
}
分区只是分组的一个特例。
还有一些不太常用的收集器,小伙伴可以点进Collectors里面看看。
接下来我们来看看规约与汇总。
以上两句话就概括出了规约和汇总的本质性区别。那好,让我们来看看什么规约?
咱们有一个List的集合,现在我可以根据这个集合求它的最大值,求它的最小值,包括求集合里所有元素的和。但是它们都有一个共同点就是它们只返回一个数据,它们不会返回另一个集合。这个呢就是规约操作。
我们通过一张图来看看规约操作都干了什么? 可以看到上面的绿色的是集合中的每一个元素,那规约呢,我这里以累加来作为例子,那规约操作就会有一个初始值(0),然后累加第一个集合中的元素生成一个值(3),接着再累加第二个元素再生成一个值(12),接着以此类推,一直到这个流结束完生成的这个值(30),就是这个流累加之后返回的一个结果。那这个就是规约操作的一个本质。
那我们再来看看,reduce接口它的一些参数和意义。
我们可以看到,这个是reduce方法最复杂的接口的定义,第一个参数呢,是初始值。我们上一张图看到的最开始的那个0就是一个初始值,当然,如果我最开始不希望它是0,那我也可以给它赋成别的值作为一个初始值,第二个参数就是计算逻辑,那咱们上一张图的加就是它的计算逻辑,初始值加第一个元素,中间运算的这个加法就是它的计算逻辑,我们再来看第三个参数,并行执行时多个部分结果的合并方式,要说到这个就应该会说到一个分治思想的例子,就是说我这个Stream里面的流中的元素我可以进行一部分一部分的计算,最后再把这个计算进行汇总,这个就是分治思想的体现。以上就是reduce中三个参数的意义。
下面我们再结合一张图看看这三个参数都是干嘛用的。
可以看到右下角的0就是我们说的第一个参数初始值,然后加号就是连个元素进行操作的计算逻辑,再来看红框框起来的两部分,这两部分就是分别进行的并行计算,最后会有一个合并的方式,就是第三个参数。相信通过这张图,大家可以清晰得理解到规约操作都会涉及到哪三个步骤。
接下来让我们通过一个实战来看一看,如何根据一批订单信息,计算平均商品价格。我们来使用最难的这种reduce的三个参数的方法来计算。