前言
1.流 (Stream)是什么
流是Java8引入的新成员,以声明方式处理集合数据,将基础操作链接起来,完成复杂的数据处理流水线,提供透明的并行处理能力。
即从支持数据处理操作的源生成的元素序列,流的来源,可以是集合,数组等。
2. 流与集合的区别
(1). 时间与空间
集合是一个内存中(空间)的数据结构,它需要集合中的每个元素都得算出来(时间)才能添加到集合里(例如add,remove方法,但每个元素都是放在内存里,得算出来之后才能在集合里)。
例如:我们大家都去吃顿饭,等待一个一个菜的上,但是我们又要拍照,所以要等待时间上齐所有菜,这样让我们拍完就可以开始吃哩。
流则是在概念上固定(空间)的数据结构(不能add或者remove元素),其元素则是按需(时间)计算的。
例如:回转寿司,一个寿司接着一个寿司,当寿司来的时候你只需要到的时候想着要吃哪个口味的寿司就好了。
(2).流只遍历一次
和迭代器类似,流只能遍历一次。
(3).外部迭代与内部迭代
外部迭代:使用Collection接口需要用户去做迭代(比如用for-each)。
内部迭代:它帮你把迭代做了,还把得到的流值存在了某个地方,你只要给出一个函数说要干什么就可以了,并且能实现数据并行。
3. 流的组成三要素
(1).数据源、(2).中间操作、(3).终端操作
Set<String> set = list.stream() //数据源
.map(sku -> sku.getSkuName()) //中间操作,会返回一个流,只遍历一次
.collect(Collectors.toSet()); // 终端操作
//终端操作是整个流操作的开关,没有终端操作,中间操作就不会执行。
4.流的操作分类
包含中间操作、终端操作。
(1).中间操作是指会返回一个流,每个流都只能遍历一次。既包含(无状态、有状态操作)。
中间操作(无状态):filter、map、flatMap、peek等。
中间操作(有状态):distinct、skip、limit、sorted等。
(2).终端操作是整个流操作的开关,没有终端操作,中间操作就不会执行。既包含(非短路、短路操作)。
终端操作(短路): allMatch、anyMatch、noneMatch、findFirst、findAny等。
终端操作(非短路):collect、forEach、sum、count、max、min等。
5.流的使用
准备好一个练习demo
(1).先创建一个商品类
public class Product {
// 编号
private Integer proId;
// 商品名称
private String proName;
// 单价
private Double proPrice;
// 购买个数
private Integer totalNum;
// 总价
private Double totalPrice;
// 商品类型
private Enum proCategory;
/**
* 构造函数
* @param proId
* @param proName
* @param proPrice
* @param totalNum
* @param totalPrice
* @param proCategory
*/
public Sku(Integer proId, String proName,
Double proPrice, Integer totalNum,
Double totalPrice, Enum proCategory) {
this.proId = proId;
this.proName = proName;
this.proPrice = proPrice;
this.proNum = totalNum;
this.totalPrice = totalPrice;
this.proCategory = proCategory;
}
}
(2).创建一个枚举类
public enum ProCategoryEnum {
CLOTHING(10, "服装类"),
ELECTRONICS(20, "数码类"),
SPORTS(30, "运动类"),
BOOKS(40, "图书类");
// 商品类型的编号
private Integer code;
// 商品类型的名称
private String name;
/**
* 构造函数
* @param code
* @param name
*/
ProCategoryEnum(Integer code, String name) {
this.code = code;
this.name = name;
}
}
(3).再创建一个CartService
public class CartService {
// 加入到购物车中的商品信息
private static List<Product> cartProList =
new ArrayList<Product>(){
{
add(new Product(654032, "无人机",
4999.00, 1,
4999.00, ProCategoryEnum.ELECTRONICS));
add(new Product(642934, "VR一体机",
2299.00, 1,
2299.00, ProCategoryEnum.ELECTRONICS));
add(new Product(645321, "纯色衬衫",
409.00, 3,
1227.00, ProCategoryEnum.CLOTHING));
add(new Product(654327, "牛仔裤",
528.00, 1,
528.00, ProCategoryEnum.CLOTHING));
add(new Product(675489, "跑步机",
2699.00, 1,
2699.00, ProCategoryEnum.SPORTS));
add(new Product(644564, "Java编程思想",
79.80, 1,
79.80, ProCategoryEnum.BOOKS));
add(new Product(678678, "Java核心技术",
149.00, 1,
149.00, ProCategoryEnum.BOOKS));
add(new Product(697894, "算法",
78.20, 1,
78.20, ProCategoryEnum.BOOKS));
add(new Product(696968, "TensorFlow进阶指南",
85.10, 1,
85.10, ProCategoryEnum.BOOKS));
}
};
/**
* 获取商品信息列表
* @return
*/
public static List<Product> getCartProList() {
return cartProList;
}
}
*(5.1).流的操作-filter
filter 使用: 过滤掉不符合断言判断的数据
public class StreamOperator {
List<Product> list = CartService.getCartProList();
@Before
public void init(){
list = CartService.getCartProList();
}
/**
* filter 使用: 过滤掉不符合断言判断的数据
*/
public void filterTest(){
list.stream()
// filter
.filter(pro -> ProCategoryEnum.BOOKS.equals(pro.getProCategory()))
.forEach(item ->
System.out.println(
JSON.toJSONString(
item,true)));
}
}
// 输出结果
{
"proCategory":"BOOKS",
"proId":644564,
"proName":"Java编程思想",
"proPrice":79.8,
"totalNum":1,
"totalPrice":79.8
}........
.....
(5.2).流的操作-map
将一个元素转换成另一个元素
public void mapTest(){
list.stream()
.map(pro -> pro.getProName())
.forEach(item -> System.out.println(
JSON.toJSONString(
item,true)));
}
// 输出结果
"无人机"
"VR一体机"
"纯色衬衫"
"牛仔裤"
"跑步机"
"Java编程思想"
"Java核心技术"
"算法"
"TensorFlow进阶指南"
(5.3).流的操作-flatMap
flatMap使用: 将一个对象转换成流
public void flatMapTest(){
list.stream()
//flatMap
.flatMap(pro -> Arrays.stream(pro.getProductName().split("")))
.forEach(item -> System.out.println(
JSON.toJSONString(
item,true)));
}
// 输出结果
"无"
"人"
"机"
"V"
"R"
"一"
"体"
"机"
"纯"
"色"
.....
(5.4).流的操作-peek
peek使用: 对流中元素进行遍历操作,与forEach类似,但不会销毁元素
public void peekTest(){
list.stream()
//peek
.peek(pro ->
System.out.println(pro.getProName()))
.forEach(item ->
System.out.println(
JSON.toJSONString(item,true)));
}
//输出结果
无人机
{
"proCategory":"ELECTRONICS",
"proId":654032,
"proName":"无人机",
"proPrice":4999.0,
"totalNum":1,
"totalPrice":4999.0
}
VR一体机....
....
(5.5).流的操作-sort
sort使用: 对流中元素进行排序,可选择自然排序或指定排序规则 有状态操作
public void sortTest(){
list.stream()
.peek(pro -> System.out.println(pro.getSkuName()))
//sort
.sorted(
Comparator.comparing(Product::getTotalPrice))
.forEach(item ->
System.out.println(JSON.toJSONString(item,true)));
}
//输出结果
无人机
VR一体机
纯色衬衫
牛仔裤
跑步机
Java编程思想
Java核心技术
算法
TensorFlow进阶指南
{
"proCategory":"BOOKS",
"proId":697894,
"proName":"算法",
"proPrice":78.2,
"totalNum":1,
"totalPrice":78.2
}....
....
(5.6).流的操作-distinct
distinct使用:去除重复的元素
public void distinctTest(){
list.stream()
.map(pro -> pro.getSkuCategory())
// distinct
.distinct()
.forEach(item ->
System.out.println(JSON.toJSONString(item,true)));
}
//输出结果
"ELECTRONICS"
"CLOTHING"
"SPORTS"
"BOOKS"
(5.7).流的操作-skip
skip:使用:跳过前N条记录。有状态操作
public void skipTest(){
list.stream()
.sorted(Comparator.comparing(Product::getTotalPrice))
.skip(3)
.forEach(item -> System.out.println(JSON.toJSONString(item,true)));
}
//输出结果
{
"proCategory":"BOOKS",
"proId":678678,
"proName":"Java核心技术",
"proPrice":149.0,
"totalNum":1,
"totalPrice":149.0
}
{
"proCategory":"CLOTHING",
"proId":654327,
"proName":"牛仔裤",
"proPrice":528.0,
"totalNum":1,
"totalPrice":528.0
}
....
...
(5.8).流的操作-limit
lmit使用: 截断前N条记录
public void limiTest(){
list.stream()
.sorted(Comparator.comparing(Product::getTotalPrice))
// limit
// 制作假分页
.skip(0 * 3) //跳过前三条 0 .skip(1 * 3 )以此类推
.limit(3) // 3 条数据
.forEach(item -> System.out.println(JSON.toJSONString(item,true)));
}
//输出结果
(5.9).流的操作-allMatch
allMatch使用: 终端操作,短路操作.所有元素匹配返回true
public void allMatchTest(){
boolean match =
list.stream()
//allMatch
.peek(pro -> System.out.println(pro.getProName()))
.allMatch(pro -> pro.getTotalPrice() > 100);
System.out.println(match);
}
//输出结果
无人机
VR一体机
纯色衬衫
牛仔裤
跑步机
Java编程思想
false
(5.10).流的操作-anyMatch
anyMatch使用: 任何元素匹配,返回true
public void anyMatchTest(){
boolean match = list.stream()
//anyMatch
.peek(pro -> System.out.println(pro.getSkuName()))
.anyMatch(pro -> pro.getTotalPrice() > 100);
System.out.println(match);
Set<String> set = list.stream().map(pro -> pro.getProName()).collect(Collectors.toSet());
}
//输出结果
无人机
true
(5.11).流的操作-noneMatch
noneMatchTest 使用: 任务元素都不匹配,返回true
public void noneMatchTest(){
boolean match = list.stream()
.peek(sku -> System.out.println(pro.getProName()))
//noneMatch
.noneMatch(pro -> pro.getTotalPrice() > 10_000);
System.out.println(match);
}
//输出结果
无人机
VR一体机
纯色衬衫
....
....
true
(5.12).流的操作-findFirst
findFirst使用:找到第一个
public void findFirstTest(){
Optional<Product> otlPro
= list.stream()
//findFirst
.findFirst(); System.out.println(JSON.toJSONString(otlPro.get(),true));
}
//输出结果
{
"proCategory":"ELECTRONICS",
"proId":654032,
"proName":"无人机",
"proPrice":4999.0,
"totalNum":1,
"totalPrice":4999.0
}
(5.13).流的操作-findAny
findAny使用:找到任意一个
public void findAnyTest(){
Optional<Product> otlPro
= list.stream()
// findAny
.findAny(); System.out.println(JSON.toJSONString(otlPro.get(),true));
}
//输出结果
{
"proCategory":"ELECTRONICS",
"proId":654032,
"proName":"无人机",
"proPrice":4999.0,
"totalNum":1,
"totalPrice":4999.0
}
(5.14).流的操作-max
max使用: 找出最大值
public void maxTest(){
OptionalDouble optionalDouble =
list.stream()
//获取总价
.mapToDouble(Product::getTotalPrice)
.max();
System.out.println(optionalDouble.getAsDouble());
}
//输出结果
4999.0
(5.15).流的操作-min
min使用: 找出最小值
public void minTest(){
OptionalDouble optionalDouble =
list.stream()
//获取总价
.mapToDouble(Product::getTotalPrice)
.min() ;
System.out.println(optionalDouble.getAsDouble());
}
//输出结果
78.2
(5.16).流的操作-count
count使用: 总计数量
public void countTest(){
long count = list.stream()
.count();
System.out.println(count);
}
//输出结果
9
测试 main主函数
public static void main(String args[]){
StreamOperator streamOperator = new StreamOperator();
streamOperator.countTest();
}
6.结语
大致就把基本用到Stream流梳理完,该文章是通过某课视频学习而练习总结做记录的应用Demo。