为什么需要 Stream
Stream 作为 Java 8 的一大亮点,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。
- JDK1.8引入的新成员,以声明式方式处理集合数据
- 将基础操作连接起来,完成复杂的数据处理流水线
- 提供透明的并行处理
流与集合的区别
- 时间与空间
- 只能遍历一次
- 外部迭代与内部迭代
对比:原始集合操作与Stream集合操作 (过滤/映射/扁平化/遍历/排序/去重/跳过/截断的应用)
流的组成
流操作的分类
流的使用
原始集合操作与Stream集合操作 (过滤/映射/扁平化/遍历/排序/去重/跳过/截断的应用)
public static void main(String[] args) {
List<StreetAlarmReportVo> voList = new ArrayList<>();
StreetAlarmReportVo vo = null;
for (int i = 1; i <= 3; i++) {
vo = new StreetAlarmReportVo();
vo.setPlaceName("地点" + i);
vo.setStreetName("南京街");
vo.setPlaceTypeName("人流量较大的街");
vo.setFlowNum(i); // 人流量
vo.setScanNum(i); //扫码人数
voList.add(vo);
}
/**
* forEach使用:
* 循环
*/
voList.stream().forEach(v ->{
v.setFlowNum(v.getFlowNum() + 1000);
});
/**
* map使用:
* 将一个元素转换成另一个元素,创建一个新的集合
*/
List<String> streetNameList = voList.stream().map(v -> v.getStreetName()).collect(Collectors.toList());
System.out.println(JSON.toJSONString(streetNameList));
/**
* 不用stream流的处理
*/
List<String> streetStrList = new ArrayList<>();
for (String streetName : streetNameList){
streetStrList.add(streetName);
}
System.out.println(JSON.toJSONString(streetStrList));
/**
* filter使用:
* 过滤出集合中符合条件的对象
*/
voList.stream().filter(v -> v.getStreetName().equals("街道1")).forEach(v -> {
System.out.println(JSON.toJSONString(v));
});
List<String> streetList = voList
.stream().filter(v -> v.getStreetName().equals("街道1"))
.map(v -> v.getStreetName()).collect(Collectors.toList());
System.out.println(JSON.toJSONString(streetList));
/**
* sorted使用:
* 顺序排列,加上reversed()方法为倒叙
*/
voList.stream().sorted(Comparator.comparing(StreetAlarmReportVo::getFlowNum).reversed()).forEach(v -> {
System.out.println(v.getFlowNum());
});
System.out.println(JSON.toJSONString(voList.get(0)));
/**
* list排序
* 顺序排列,加上reversed()方法为倒叙
* 使用 Comparator.nullsFirst进行排序,当集合中存在null元素时,可以使用针对null友好的比较器,null元素排在集合的最前面
* 使用 Comparator.thenComparing 排序,首先使用 flowNum 排序,紧接着在使用scanNum 排序
*/
voList.sort(Comparator.comparing(StreetAlarmReportVo::getFlowNum).reversed());
System.out.println("顺序排列,加上reversed()方法为倒叙:" + JSON.toJSONString(voList));
voList.sort(Comparator.nullsFirst(Comparator.comparing(StreetAlarmReportVo::getFlowNum)).reversed());
System.out.println("使用 Comparator.nullsFirst进行排序:" + JSON.toJSONString(voList));
voList.sort(Comparator.comparing(StreetAlarmReportVo::getFlowNum).reversed().thenComparing(StreetAlarmReportVo::getScanNum));
System.out.println("使用 Comparator.thenComparing 排序:" + JSON.toJSONString(voList));
/**
* distinct使用:
* 对流元素进行去重。有状态操作
*/
List<String> streets = voList.stream().map(v -> v.getStreetName()).distinct().collect(Collectors.toList());
System.out.println("去重后的元素:" + JSON.toJSONString(streets));
/**
* skip使用:
* 跳过前N条记录,有状态操作
*/
List<StreetAlarmReportVo> voList2 = voList.stream().skip(2).collect(Collectors.toList());
System.out.println("使用skip跳过前两条记录:" + JSON.toJSONString(voList2));
/**
* limit使用:
* 截断前N条记录。有状态操作
*/
List<StreetAlarmReportVo> voList3 = voList.stream().limit(2).collect(Collectors.toList());
System.out.println("limit截断前2条记录:" + JSON.toJSONString(voList3));
/**
* allMatch使用:
* 终端操作,短路操作。所有元素匹配,返回true
* anyMatch使用:
* 任何元素匹配,返回true
*/
boolean flag = voList.stream().allMatch(v -> v.getStreetName().equals("南京街"));
boolean flag2 = voList.stream().anyMatch(v -> v.getStreetName().equals("南京街"));
System.out.println("集合内的街道名称是否都是南京街:" + flag);
System.out.println("集合内的街道名称是否有一个是南京街:" + flag);
}
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使用:找到第一个
@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使用:找任意一个
/**
* 找任意一个
*/
@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);
}
}
流的四种构建形式
由数值直接构建流
@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);
}
通过文件生成流
@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() {
// 2,4,6,8...一直无限下去
// Stream stream = Stream.iterate(0, n -> n + 2);
Stream stream = Stream.generate(Math::random);
stream.limit(100)
.forEach(System.out::println);
}
流的预定义收集器
集合收集器
/**
* 集合收集器
*/
@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));
}
分组与分区的区别
归约与汇总操作(涉及到并行执行)
归约reduce
@Test
public void reduceTest() {
/**
* 订单对象
*/
@Data
@AllArgsConstructor
class Order {
/**
* 订单编号
*/
private Integer id;
/**
* 商品数量
*/
private Integer productCount;
/**
* 消费总金额
*/
private Double totalAmount;
}
/*
准备数据
*/
ArrayList<Order> list = Lists.newArrayList();
list.add(new Order(1, 2, 25.12));
list.add(new Order(2, 5, 257.23));
list.add(new Order(3, 3, 23332.12));
/*
以前的方式:
1. 计算商品数量
2. 计算消费总金额
*/
/*
汇总商品数量和总金额
*/
Order order = list.stream()
.parallel()
.reduce(
// 初始化值
new Order(0, 0, 0.0),
// Stream中两个元素的计算逻辑
(Order order1, Order order2) -> {
System.out.println("执行 计算逻辑 方法!!!");
int productCount =
order1.getProductCount()
+ order2.getProductCount();
double totalAmount =
order1.getTotalAmount()
+ order2.getTotalAmount();
return new Order(0, productCount, totalAmount);
},
// 并行情况下,多个并行结果如何合并
(Order order1, Order order2) -> {
System.out.println("执行 合并 方法!!!");
int productCount =
order1.getProductCount()
+ order2.getProductCount();
double totalAmount =
order1.getTotalAmount()
+ order2.getTotalAmount();
return new Order(0, productCount, totalAmount);
});
System.out.println(JSON.toJSONString(order, true));
}
运行结果
执行 计算逻辑 方法!!!
执行 计算逻辑 方法!!!
执行 计算逻辑 方法!!!
执行 合并 方法!!!
执行 合并 方法!!!
{
"id":0,
"productCount":10,
"totalAmount":23614.469999999998
}
汇总collect
@Test
public void collectTest() {
/**
* 订单对象
*/
@Data
@AllArgsConstructor
class Order {
/**
* 订单编号
*/
private Integer id;
/**
* 用户账号
*/
private String account;
/**
* 商品数量
*/
private Integer productCount;
/**
* 消费总金额
*/
private Double totalAmount;
}
/*
准备数据
*/
ArrayList<Order> list = Lists.newArrayList();
list.add(new Order(1, "zhangxiaoxi", 2, 25.12));
list.add(new Order(2, "zhangxiaoxi",5, 257.23));
list.add(new Order(3, "lisi",3, 23332.12));
/*
Map<用户账号, 订单(数量和金额)>
*/
Map<String, Order> collect = list.stream()
.parallel()
.collect(
() -> {
System.out.println("执行 初始化容器 操作!!!");
return new HashMap<String, Order>();
},
(HashMap<String, Order> map, Order newOrder) -> {
System.out.println("执行 新元素添加到容器 操作!!!");
/*
新元素的account已经在map中存在了
不存在
*/
String account = newOrder.getAccount();
// 如果此账号已存在,将新订单数据累加上
if (map.containsKey(account)) {
Order order = map.get(account);
order.setProductCount(
newOrder.getProductCount()
+ order.getProductCount());
order.setTotalAmount(
newOrder.getTotalAmount()
+ order.getTotalAmount());
} else {
// 如果不存在,直接将新订单存入map
map.put(account, newOrder);
}
}, (HashMap<String, Order> map1, HashMap<String, Order> map2) -> {
System.out.println("执行 并行结果合并 操作!!!");
map2.forEach((key, value) -> {
map1.merge(key, value, (order1, order2) -> {
// TODO 注意:一定要用map1做合并,因为最后collect返回的是map1
return new Order(0, key,
order1.getProductCount()
+ order2.getProductCount(),
order1.getTotalAmount()
+ order2.getTotalAmount());
});
});
});
System.out.println(JSON.toJSONString(collect, true));
}
运行结果
执行 初始化容器 操作!!!
执行 初始化容器 操作!!!
执行 初始化容器 操作!!!
执行 新元素添加到容器 操作!!!
执行 新元素添加到容器 操作!!!
执行 新元素添加到容器 操作!!!
执行 并行结果合并 操作!!!
执行 并行结果合并 操作!!!
{
"lisi":{
"account":"lisi",
"id":3,
"productCount":3,
"totalAmount":23332.12
},
"zhangxiaoxi":{
"account":"zhangxiaoxi",
"id":0,
"productCount":7,
"totalAmount":282.35
}
}