Stream 流的简介
一句话简介:从支持数据处理操作的源生成的元素序列。
数据处理操作:类似数据库的操作。
源:数据。
元素序列:流提供了可以访问特定元素有序值的接口。
流的组成
组成:数据源 + 中间操作 + 终端操作
流操作分类
有状态操作:需要建立在所有的数据基础上,也就是需要等待前面的流处理完后,再进行统一的操作。
无状态操作:对单个元素进行处理。
短路操作:一个元素满足条件后,后面的就不用执行。
非短路操作:数据集中的每个数据都要执行一遍。
注:终端操作结束后流就不可用了。
流的使用
在开始使用流之前,先做好准备工作:
- 创建一个实体类,作为数据的来源
public class Sku {
private Integer skuId;
private String skuName;
private Double skuPrice;
// 购买个数
private Integer totalNum;
private Double totalPrice;
// 商品类型
private Enum skuCategory;
public Sku(Integer skuId, String skuName, Double skuPrice, Integer totalNum, Double totalPrice, Enum skuCategory) {
this.skuId = skuId;
this.skuName = skuName;
this.skuPrice = skuPrice;
this.totalNum = totalNum;
this.totalPrice = totalPrice;
this.skuCategory = skuCategory;
}
@Override
public String toString() {
return "Sku{" +
"skuId=" + skuId +
", skuName='" + skuName + '\'' +
", skuPrice=" + skuPrice +
", totalNum=" + totalNum +
", totalPrice=" + totalPrice +
", skuCategory=" + skuCategory +
'}';
}
public Integer getSkuId() {
return skuId;
}
public void setSkuId(Integer skuId) {
this.skuId = skuId;
}
public String getSkuName() {
return skuName;
}
public void setSkuName(String skuName) {
this.skuName = skuName;
}
public Double getSkuPrice() {
return skuPrice;
}
public void setSkuPrice(Double skuPrice) {
this.skuPrice = skuPrice;
}
public Integer getTotalNum() {
return totalNum;
}
public void setTotalNum(Integer totalNum) {
this.totalNum = totalNum;
}
public Double getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(Double totalPrice) {
this.totalPrice = totalPrice;
}
public Enum getSkuCategory() {
return skuCategory;
}
public void setSkuCategory(Enum skuCategory) {
this.skuCategory = skuCategory;
}
}
- 定义一个枚举类,里面放商品类型
public enum SkuCategoryEnum {
CLOTHING(10, "服装类"),
SELECTRONICE(20, "数码类"),
SPORTS(30, "运动类"),
BOOKS(40, "图书类");
// 商品类型编号
private Integer code;
// 商品类型名称
private String name;
SkuCategoryEnum(Integer code, String name) {
this.code = code;
this.name = name;
}
}
- 定义一个Service 类
public class CartService {
// 所有添加到购物车的商品
private static List<Sku> cartSkuList = new ArrayList<Sku>() {
{
add(new Sku(654032, "大疆无人机", 4999.00, 1,
4999.00, SkuCategoryEnum.SELECTRONICE));
add(new Sku(467325, "VR-体机", 8999.00, 1,
8999.00, SkuCategoryEnum.SELECTRONICE));
add(new Sku(967325, "索尼相机", 12000.00, 2,
24000.00, SkuCategoryEnum.SELECTRONICE));
add(new Sku(856325, "Java并发编程", 50.00, 3,
150.00, SkuCategoryEnum.BOOKS));
add(new Sku(654325, "Oracle入门", 45.00, 2,
90.00, SkuCategoryEnum.BOOKS));
add(new Sku(145674, "耐克T恤", 100.00, 4,
400.00, SkuCategoryEnum.CLOTHING));
add(new Sku(142564, "阿迪达斯T恤", 120.00, 2,
400.00, SkuCategoryEnum.CLOTHING));
add(new Sku(356325, "篮球", 350.00, 2,
700.00, SkuCategoryEnum.SPORTS));
add(new Sku(568410, "羽毛球", 1500.00, 2,
3000.00, SkuCategoryEnum.SPORTS));
}
};
// 获取商品信息列表
public static List<Sku> getCartSkuList() {
return cartSkuList;
}
}
- 定义流操作类
public class StreamOperator {
List<Sku> list;
@Before
public void init() {
list = CartService.getCartSkuList();
}
@Test
public void filterTest() {
list.stream()
// 不是图书类的过滤掉
.filter(sku -> sku.getSkuCategory().equals(SkuCategoryEnum.BOOKS))
// 打印中间操作的结果集
.forEach(item -> System.out.println(JSON.toJSONString(item, true)));
}
// map:将一个元素转换成另一个类型的元素
@Test
public void mapTest() {
list.stream()
// 将 Sku 类型的集合转换成 String 类型的集合
.map(sku -> sku.getSkuName())
// 打印中间操作的结果集
.forEach(item -> System.out.println(JSON.toJSONString(item, true)));
}
// 扁平化map 将对象转换成流
@Test
public void flatMapTest() {
list.stream()
// 接收一个元素,返回一个新的流,并且与其他元素产生的流合并,同一交给终端操作
.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 与 forEach 交替执行,因为后面的forEach是无状态的
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach(item -> System.out.println( JSON.toJSONString(item, true)));
}
/**
* sort使用:对流中元素进行排序,可选则自然排序或指定排序规则。有状态操作
*/
@Test
public void sortTest() {
list.stream()
// 此时的 peek 不会和后面的 forEach交替操作,因为后面的sorted是有状态的
.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))
// 过滤掉前 3 条数据
.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,否则返回false
*/
@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 和 findFirst 没有区别,返回的值是一样的,
* 若是并行操作,findAny 可能随机返回一个值,在并行上面比 findFirst快
*/
@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()
// 获取总价 将一个元素映射成 Double 类型的元素
.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);
}
}
流的构建
流有4种构建方式:由值、数组、文件、函数创建流。
1.创建 StreamConstructor 类
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 = "...\\CartService.java";
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);
}
}
收集器
作用:将流中的元素累积成一个结果,作用于终端操作collect()。
collect:是一个终端操作,是流收集的最后一个步骤,是一个方法,接收Collector接口实现类。
Collector:是一个接口。
Collectors:是一个工具类,里面定义了一些收集器。
预定义收集器功能
- 将流元素归约和汇总成一个值。
- 将流元素分组。
- 将流元素分区。
代码演示:
public class StreamCollector {
/**
* 集合收集器
*/
@Test
public void toList() {
List<Sku> list = CartService.getCartSkuList();
List<Sku> result = list.stream()
.filter(sku -> sku.getTotalPrice() > 100)
// 将集合中的元素收集成 list 返回
.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));
}
/**
* 分区,分组的特殊形式,将数据根据boolean值分为两组,分别是true和false
*/
@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));
}
}
归约与汇总
待补充…