流是什么
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素队列:特定类型的对象形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源:流的来源。可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作:类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
流的组成
+--------------------+ +------+ +------+ +----+ +--------+
| stream of elements |-----> |filter|-> |sorted|-> |map |-> |collect |
+--------------------+ +------+ +------+ +----+ +--------+
| 数据源 |-----> | 中间操作 |-> |终端操作 |
+--------------------+ +------+ +------+ +----+ +--------+
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
流操作分类
- 中间操作(intermediate operation)
- 无状态操作
- 过滤(filter)
- 映射(map)
- 扁平化(flatMap)
- 遍历(peek)
- 有状态操作
- 去重(distinct)
- 跳过(slip)
- 截断(limit)
- 排序(sorted)
- 无状态操作
- 终端操作(terminal operation)
- 非短路操作
- 遍历(forEach)
- 归约(reduce)
- 最大值(max)
- 最小值(min)
- 聚合(collect)
- 计数(count等)
- 短路操作
- 所有匹配(allMatch)
- 任意匹配(anyMatch)
- 不匹配(noMatch)
- 查找首个(findFirst)
- 查找任意(findAny)
- 非短路操作
实例
/**
* @author fangliu
* @date 2020-02-13
* @description 演示流的各种操作
*/
public class StreamOperatorTest {
/**
* 模拟测试数据
*/
private static List<Goods> goodsList= new ArrayList<Goods>(){
{
add(new Goods(111,"无人机", GoodTypeEnum.DIGITAL,10000.00, 10000.00,1));
add(new Goods(112,"VR一体机", GoodTypeEnum.DIGITAL,13000.00, 13000.00,1));
add(new Goods(113,"衬衫", GoodTypeEnum.APPAREL,100.00, 300.00,3));
add(new Goods(114,"牛仔裤", GoodTypeEnum.APPAREL,120.00, 120.00,1));
add(new Goods(115,"Java编程思想", GoodTypeEnum.BOOKS,80.00, 80.00,1));
add(new Goods(116,"Java核心技术", GoodTypeEnum.BOOKS,90.00, 90.00,1));
add(new Goods(117,"算法", GoodTypeEnum.BOOKS,60.00, 60.00,1));
add(new Goods(118,"跑步机", GoodTypeEnum.SPORTS,3600.00, 3600.00,1));
}
};
/**
* forEach 来迭代流中的每个元素
*/
@Test
public void forEachTest(){
goodsList.stream()
.forEach(goods->System.out.println(JSON.toJSONString(goods)));
}
/**
* filter 通过设置的条件过滤出元素
*/
@Test
public void filterTest(){
goodsList.stream()
// 过滤出商品中的图书类
.filter(goods-> GoodTypeEnum.BOOKS.equals(goods.getGoodType()))
.forEach(goods->System.out.println(JSON.toJSONString(goods)));
}
/**
* map 映射每个元素到对应的结果
*/
@Test
public void mapTest(){
goodsList.stream()
// 将商品中名字映射到结果中
.map(goods-> goods.getGoodName())
.forEach(goods->System.out.println(JSON.toJSONString(goods,true)));
}
/**
* flatMap 将一个对象转换成流
*/
@Test
public void flatMapTest(){
goodsList.stream()
// 将商品中名字转换成流
.flatMap(goods-> Arrays.stream(goods.getGoodName().split("")))
.forEach(goods->System.out.println(JSON.toJSONString(goods,true)));
}
/**
* peek 来迭代流中的每个元素,与forEach相似,但不会销毁流元素
*/
@Test
public void peekTest(){
goodsList.stream()
// 迭代商品中的商品名字
.peek(goods-> System.out.println(goods.getGoodName()))
.forEach(goods->System.out.println(JSON.toJSONString(goods,true)));
}
/**
* sorted 对流中的元素进行排序 可选择自然排序或者排序规则
*/
@Test
public void sortedTest(){
goodsList.stream()
// 按商品的价格进行排序
.sorted(Comparator.comparing(goods -> goods.getGoodPrice()))
.forEach(goods->System.out.println(JSON.toJSONString(goods)));
}
/**
* distinct 对流中的元素去重
*/
@Test
public void distinctTest(){
goodsList.stream()
// 将商品类型映射到结果中
.map(goods ->goods.getGoodType())
//去重
.distinct()
.forEach(goods->System.out.println(JSON.toJSONString(goods)));
}
/**
* skip 跳过前N条元素
*/
@Test
public void skipTest(){
goodsList.stream()
// 按商品的价格进行排序,
.sorted(Comparator.comparing(goods -> goods.getGoodPrice()))
// 跳过前两条
.skip(2)
.forEach(goods->System.out.println(JSON.toJSONString(goods)));
}
/**
* limit 截断前N条元素
*/
@Test
public void limitTest(){
goodsList.stream()
// 按商品的价格进行排序,
.sorted(Comparator.comparing(goods -> goods.getGoodPrice()))
// 截断前两条
.limit(2)
.forEach(goods->System.out.println(JSON.toJSONString(goods)));
}
/***********************************************/
/**
*allMatch 必须全部都满足才会返回true
*/
@Test
public void allMatchTest(){
boolean allMatch =goodsList.stream()
.peek(goods->System.out.println(JSON.toJSONString(goods)))
// 商品单价大于500
.allMatch(goods -> goods.getGoodPrice()>500);
System.out.println(allMatch);
}
/**
*anyMatch 只要有一个条件满足即返回true
*/
@Test
public void anyMatchTest(){
boolean allMatch =goodsList.stream()
.peek(goods->System.out.println(JSON.toJSONString(goods)))
// 商品单价大于1000
.anyMatch(goods -> goods.getGoodPrice()>1000);
System.out.println(allMatch);
}
/**
*noneMatch 全都不满足才会返回true
*/
@Test
public void noneMatchTest(){
boolean allMatch =goodsList.stream()
.peek(goods->System.out.println(JSON.toJSONString(goods)))
// 商品单价大于10000
.noneMatch(goods -> goods.getGoodPrice()>10000);
System.out.println(allMatch);
}
/**
*findFirst 找到第一个元素
*/
@Test
public void findFirstTest(){
Optional optional =goodsList.stream()
.findFirst();
System.out.println(JSON.toJSONString(optional.get()));
}
/**
*findAny 找到任意一个元素
*/
@Test
public void findAnyTest(){
for (int i = 0; i < 20; i++) {
Optional optional =goodsList.stream()
.findAny();
System.out.println(JSON.toJSONString(optional.get()));
}
}
/**
* mapToInt/mapToLong/mapToDouble 主要用于int、double、long等基本类型上,进行统计结果
*/
@Test
public void mapToXXTest(){
DoubleSummaryStatistics stats = goodsList.stream()
// 将商品价格映射到流中
.map(goods ->goods.getGoodPrice())
.mapToDouble((x)-> x).summaryStatistics();
System.out.println("商品中价格最贵的商品 : " + stats.getMax());
System.out.println("商品中价格最便宜的商品 : " + stats.getMin());
System.out.println("所有商品的价格之和 : " + stats.getSum());
System.out.println("商品的平均数 : " + stats.getAverage());
}
}
流的构建
- 由值创建流
- 由数组创建流
- 由文件创建流
- 由函数生产流
实例
/**
* @author fangliu
* @date 2020-02-14
* @description 流的四种构建形式
*/
public class StreamConstructor {
/**
* 由值创建流
*/
@Test
public void streamFromValue(){
Stream stream = Stream.of(1, 2, 3, 4, 5, 6);
stream.forEach(System.out::println);
}
/**
* 由数组创建流
*/
@Test
public void streamFromArrays(){
int[] numbers = {1, 2, 3, 4, 5, 6};
IntStream stream = Arrays.stream(numbers);
stream.forEach(System.out::println);
}
/**
* 由文件创建流
*/
@Test
public void streamFromFiles() throws IOException {
String path = "/Users/fangliu/data/workspace/study/src/test/java/com/example/demo/stream/StreamConstructor.java";
Stream stream = Files.lines(Paths.get(path));
stream.forEach(System.out::println);
}
/**
* 由函数生产流(无限流)
*/
@Test
public void streamFromValue1(){
//Stream stream = Stream.iterate(0,n->n+2);
Stream stream = Stream.generate(Math::random);
stream.limit(100).forEach(System.out::println);
}
}
收集器
- 将流中的元素累积成一个结果
- 作用于终端操作的collect()上
- collect/Collector/Collectors
预定义收集器功能
- 将流元素归约
- 将流元素分组
- 将流元素分区
/**
* collect Collectors类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串
*/
@Test
public void collectTest(){
// 将商品价格大于1500的转换成商品集合
List<Goods> list=goodsList.stream()
.filter(goods -> goods.getGoodPrice()>1500)
.collect(Collectors.toList());
System.out.println(list);
//将商品名用逗号拼接成字符串
String str=goodsList.stream()
.map(goods -> goods.getGoodName())
.collect(Collectors.joining(","));
System.out.println(str);
// Map<分组条件,结果集合>
Map<Object,List<Goods>> group=goodsList.stream()
//按照商品类型分组
.collect(Collectors.groupingBy(goods -> goods.getGoodType()));
System.out.println(JSON.toJSONString(group,true));
// Map<分区条件,结果集合> 分区是分组的一个特例
Map<Boolean,List<Goods>> partition=goodsList.stream()
//商品大于1000的商品归为true 其余false
.collect(Collectors.partitioningBy(goods -> goods.getGoodPrice()>1000));
System.out.println(JSON.toJSONString(partition,true));
}