Stream使用小结
For my bad memory
Stream的生命周期
- 创建(加载数据) ----> 多次流操作(数据处理) ----> 返回(组装数据结构)
- 创建(加载数据) ----> 返回(组装数据结构)
创建
基于数据,通常为集合或者多个同类型对象,创建一个Stream或者Parallel Stream(并行流)
Stream本身创建后,只能被使用or消费or遍历一次,its a one way trip
可以通过Supplier<Stream
操作(intermediate ops)
对Stream内容进行相关操作,例如过滤,转换等,形成一个新的Stream
操作同时可以区分为Stateless(无状态)和Stateful(有状态)两种类型,无状态指对每一个元素的操作不依赖另一个元素(数据,状态,结果)等,有状态指每次操作会将前一个元素的操作结果向后传递,可以用于下一次操作。在使用上,简单点来说,有状态的操作,不适合parallel stream
返回(terminal ops)
将Stream的内容或者属性,或者操作结果,按照期望的数据结构进行返回,例如新的集合,元素数量或者输出等
创建和操作,都是通过Stream对象来进行的,最终需要通过返回/中断操作,将最终的数据,返回到主流程。这也是所有函数化编程的基本原则,通常配合Collections等其他的函数化方法/api,简化返回结构的创建。
Stream 的创建
通过集合对象Collection创建
常用集合list,set等集合对象提供的stream方法
/* From List */ List<String> list = new ArrayList<>(); Stream<String> listStream = list.stream();
通过数组对象Array创建
通过arrays提供的steam方法,将数组转换为Stream
/* From Array */ String [] stringArray= "abc,def,ghi".split(","); Stream<String> stream = Arrays.stream(stringArray);
通过Stream类创建
通过多个同类型对象创建一个stream
/* From Objects */ Stream<String> stream = Stream.of("abc","def","ghi"); /* From Supplier - infinite sequential unordered stream*/ Stream<Integer> stream = Stream.generate(random::nextInt);
通过包装Stream类创建
通过IntStream,DoubleStream,LongStream等将基础类型转换为Stream
/* From int(s) */ Stream<Integer> intStream = IntStream.of(1,2,3,4).boxed(); /* From long(s) */ Stream<Long> longStream = LongStream.of(1L,2L,3L,4L).boxed(); /* From double(s)*/ Stream<Double> doubleStream = DoubleStream.of(1.1,1.2,1.3,1.4).boxed();
通过文件读取类BufferedReader类创建
通过BufferedReader的lines,将文件内容转换为Stream
/* From File */ BufferedReader bufferedReader = new BufferedReader( new FileReader( "text/lineStream.txt")); Stream<String> lineStream = bufferedReader.lines();
Stream 的常用操作
操作 | 作用 | 输入数据 | 输出基准 | 状态操作 |
---|---|---|---|---|
filter | 基于给定的判断Function,判断元素是否符合,返回true or false,基于Predicate | 单个元素 | boolean=true | stateless |
map | 基于给定的Function,对每个元素进行映射,转换,操作,返回结果 | 单个元素 | 函数结果集 | stateless |
flatmap | 将Stream<Collection | 集合 | 对象流 | stateless |
limit | 只返回前n个元素 | long | 前n个元素 | stateful |
skip | 跳过前n个元素,只返回剩余元素 | long | 不包含前n个元素 | stateful |
distinct | 去除重复元素,基于元素的equals | 无 | 元素唯一 | stateful |
sorted | 基于Comparator对元素进行排序 | 无/Comparator Func | 有序的 | stateful |
数据准备
/** * Vehicle * * <p>定义车辆对象 * * @version 1.0 * @author Created by ivan at 16:30. */ @Data @Builder @AllArgsConstructor static class Vehicle { private Long id; private String brand; private Integer wheels; private String type; private BigDecimal price; private String years; private Boolean sale; } /** * 模拟数据 vehicle * * @author Created by ivan * @since 2022/9/6 16:35 */ public List<Vehicle> vehicleStream() { List<Vehicle> vehicles = new ArrayList<>(); vehicles.add( Vehicle.builder() .id(1L).brand("Mercedes-Benz").wheels(4).type("G63").price(new BigDecimal("1000000")).years("2010") .build()); vehicles.add( Vehicle.builder() .id(2L).brand("Mercedes-Benz").wheels(4).type("CLK320").price(new BigDecimal("800000")).years("2021") .build()); vehicles.add( Vehicle.builder() .id(3L).brand("BMW").wheels(4).type("M4").price(new BigDecimal("400000")).years("2020") .build()); vehicles.add( Vehicle.builder() .id(4L).brand("BMW").wheels(4).type("i8").price(new BigDecimal("1200000")).years("2022") .build()); vehicles.add( Vehicle.builder() .id(5L).brand("Audi").wheels(4).type("rs7").price(new BigDecimal("1500000")).years("2022") .build()); vehicles.add( Vehicle.builder() .id(6L).brand("Audi").wheels(4).type("a4").price(new BigDecimal("300000")).years("2000") .build()); return vehicles; }
Filter示例
public void filterOps(List<Vehicle> vehicle) { Supplier<Stream<Vehicle>> supplier = () -> vehicleStream.stream(); // 获取4个轮子的Audi supplier .get() .filter(vehicle -> vehicle.getBrand().equals("Audi") && vehicle.getWheels() == 4) .forEach(System.out::println); System.out.println(); // 获取价格 >= 100w,可替换lambda supplier .get() .filter( vehicle -> { if (vehicle.getPrice().compareTo(new BigDecimal("1000000")) >= 0) { return true; } return false; }) .forEach(System.out::println); }
输出结果:
StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null) StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=300000, years=2000, sale=null) StreamMemos.Vehicle(id=1, brand=Mercedes-Benz, wheels=4, type=G63, price=1000000, years=2010, sale=null) StreamMemos.Vehicle(id=4, brand=BMW, wheels=4, type=i8, price=1200000, years=2022, sale=null) StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null)
Map示例
public void mapOp(List<Vehicle> vehicleStream) { Supplier<Stream<Vehicle>> supplier = vehicleStream::stream; // 出售所有的Audi supplier .get() .map( vehicle -> { if (vehicle.getBrand().equals("Audi")) { vehicle.setSale(true); } return vehicle; }) .collect(Collectors.toList()) .forEach(System.out::println); System.out.println(); // 下调价格10% supplier .get() .map( vehicle -> { vehicle.setPrice(vehicle.getPrice().multiply(new BigDecimal("0.9"))); return vehicle; }) .collect(Collectors.toList()) .forEach(System.out::println); System.out.println(); // 获取id列表 supplier.get().map(Vehicle::getId).collect(Collectors.toList()).forEach(System.out::println); }
输出结果:
StreamMemos.Vehicle(id=1, brand=Mercedes-Benz, wheels=4, type=G63, price=1000000, years=2010, sale=null) StreamMemos.Vehicle(id=4, brand=BMW, wheels=4, type=i8, price=1200000, years=2022, sale=null) StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null) StreamMemos.Vehicle(id=1, brand=Mercedes-Benz, wheels=4, type=G63, price=1000000, years=2010, sale=null) StreamMemos.Vehicle(id=2, brand=Mercedes-Benz, wheels=4, type=CLK320, price=800000, years=2021, sale=null) StreamMemos.Vehicle(id=3, brand=BMW, wheels=4, type=M4, price=400000, years=2020, sale=null) StreamMemos.Vehicle(id=4, brand=BMW, wheels=4, type=i8, price=1200000, years=2022, sale=null) StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=true) StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=300000, years=2000, sale=true) StreamMemos.Vehicle(id=1, brand=Mercedes-Benz, wheels=4, type=G63, price=900000.0, years=2010, sale=null) StreamMemos.Vehicle(id=2, brand=Mercedes-Benz, wheels=4, type=CLK320, price=720000.0, years=2021, sale=null) StreamMemos.Vehicle(id=3, brand=BMW, wheels=4, type=M4, price=360000.0, years=2020, sale=null) StreamMemos.Vehicle(id=4, brand=BMW, wheels=4, type=i8, price=1080000.0, years=2022, sale=null) StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1350000.0, years=2022, sale=true) StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=270000.0, years=2000, sale=true) 1 2 3 4 5 6
FlatMap示例
/** * StreamMemos * * <p>Dealer对象,一个Dealer可以销售多种Vehicle * * @version 1.0 * @author Created by ivan at 10:59. */ @Data @Builder @AllArgsConstructor static class Dealer { private Long id; private String name; private List<Vehicle> inStock; } /** * 模拟两个Dealer dealerStream * * @return java.util.List<top.moma.example.memos.StreamMemos.Dealer> * @author Created by ivan * @since 2022/9/7 11:00 */ public List<Dealer> dealerStream() { List<Dealer> dealers = new ArrayList<>(); // All dealers.add(Dealer.builder().id(1L).name("DDKY").inStock(vehicleStream()).build()); // Only Audi dealers.add( Dealer.builder() .id(2L) .name("YMCP") .inStock( vehicleStream().stream() .filter(vehicle -> vehicle.getBrand().equals("Audi")) .collect(Collectors.toList())) .build()); return dealers; } public void flatMapOp(List<Dealer> dealers) { Supplier<Stream<Dealer>> supplier = dealers::stream; // 获取全部车型 // Map,返回2个List<Vehicle> supplier .get() .map(Dealer::getInStock) .collect(Collectors.toList()) .forEach(System.out::println); System.out.println(); // FlatMap,返回1个List<Vehicle> supplier .get() .flatMap(dealer -> dealer.getInStock().stream()) .collect(Collectors.toList()) .forEach(System.out::println); }
输出结果:
[StreamMemos.Vehicle(id=1, brand=Mercedes-Benz, wheels=4, type=G63, price=1000000, years=2010, sale=null), StreamMemos.Vehicle(id=2, brand=Mercedes-Benz, wheels=4, type=CLK320, price=800000, years=2021, sale=null), StreamMemos.Vehicle(id=3, brand=BMW, wheels=4, type=M4, price=400000, years=2020, sale=null), StreamMemos.Vehicle(id=4, brand=BMW, wheels=4, type=i8, price=1200000, years=2022, sale=null), StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null), StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=300000, years=2000, sale=null)] [StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null), StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=300000, years=2000, sale=null)] StreamMemos.Vehicle(id=1, brand=Mercedes-Benz, wheels=4, type=G63, price=1000000, years=2010, sale=null) StreamMemos.Vehicle(id=2, brand=Mercedes-Benz, wheels=4, type=CLK320, price=800000, years=2021, sale=null) StreamMemos.Vehicle(id=3, brand=BMW, wheels=4, type=M4, price=400000, years=2020, sale=null) StreamMemos.Vehicle(id=4, brand=BMW, wheels=4, type=i8, price=1200000, years=2022, sale=null) StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null) StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=300000, years=2000, sale=null) StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null) StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=300000, years=2000, sale=null)
Limit / Skip / Distinct / Sorted
public void stateOp(List<Vehicle> vehicles) { Supplier<Stream<Vehicle>> supplier = vehicles::stream; // 取价格排序的前三 supplier .get() .sorted(Comparator.comparing(Vehicle::getPrice).reversed()) .limit(3) .collect(Collectors.toList()) .forEach(System.out::println); System.out.println(); // 取价格排序除前三之外的 supplier .get() .sorted(Comparator.comparing(Vehicle::getPrice).reversed()) .skip(3) .collect(Collectors.toList()) .forEach(System.out::println); System.out.println(); // 输出所有Brand supplier .get() .map(Vehicle::getBrand) .distinct() .collect(Collectors.toList()) .forEach(System.out::println); }
输出结果:
StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null) StreamMemos.Vehicle(id=4, brand=BMW, wheels=4, type=i8, price=1200000, years=2022, sale=null) StreamMemos.Vehicle(id=1, brand=Mercedes-Benz, wheels=4, type=G63, price=1000000, years=2010, sale=null) StreamMemos.Vehicle(id=2, brand=Mercedes-Benz, wheels=4, type=CLK320, price=800000, years=2021, sale=null) StreamMemos.Vehicle(id=3, brand=BMW, wheels=4, type=M4, price=400000, years=2020, sale=null) StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=300000, years=2000, sale=null) Mercedes-Benz BMW Audi
Stream 的返回
操纵 | 作用 | 参数 | 输出 | 备注 |
---|---|---|---|---|
allMatch | Stream中的元素是否都符合给定判断条件 | 基于元素的true/false函数 | Boolean | short-circuiting |
anyMatch | Stream中的元素是否有任一符合给定判断条件 | 基于元素的true/false函数 | Boolean | short-circuiting |
noneMatch | Stream中的元素无任何一个符合给定判断条件 | 基于元素的true/false函数 | Boolean | 全loop |
collect | 将Stream中的元素,收集到一个可变的Collection对象中,便于进行后续操作 | 承载容器/集合类型,配合Collectors对象适用 | 集合对象Collection | 并行流要求输入输出均为无须的,输入参数主要为Collections的相关实现,支持joing,groupBy等 |
forEach | 对Stream中的每一个元素执行给定的action | 需要对元素执行的action | 循环执行结果 | 产生side affect |
count | 返回Stream中的元素个数 | 无 | long | 配合Filter |
min | 基于元素比较方法,返回Stream中的最小元素 | 元素的Comparator | 单个元素,Optional | |
max | 基于元素比较方法,返回Stream中的最大元素 | 元素的Comparator | 单个元素,Optional |
Match 示例
public void matchEnd(List<Vehicle> vehicleStream) { Supplier<Stream<Vehicle>> supplier = vehicleStream::stream; // Match条件,是否wheels =4 boolean ifAllMatch = supplier.get().allMatch(vehicle -> vehicle.getWheels() == 4); boolean ifAnyMatch = supplier.get().anyMatch(vehicle -> vehicle.getWheels() == 4); boolean ifNoneMatch = supplier.get().noneMatch(vehicle -> vehicle.getWheels() == 4); System.out.println("All 4 wheels: " + ifAllMatch); System.out.println("Any 4 wheels: " + ifAnyMatch); System.out.println("None 4 wheels: " + ifNoneMatch); // Match条件,是否brand equals Audi ifAllMatch = supplier.get().allMatch(vehicle -> vehicle.getBrand().equals("Audi")); ifAnyMatch = supplier.get().anyMatch(vehicle -> vehicle.getBrand().equals("Audi")); ifNoneMatch = supplier.get().noneMatch(vehicle -> vehicle.getBrand().equals("Audi")); System.out.println("All Audi: " + ifAllMatch); System.out.println("Any Audi: " + ifAnyMatch); System.out.println("None Audi: " + ifNoneMatch); }
输出结果:
All 4 wheels: true Any 4 wheels: true None 4 wheels: false All Audi: false Any Audi: true None Audi: false
Collect 示例
public void collectEnd(List<Vehicle> vehicleStream) { Supplier<Stream<Vehicle>> supplier = vehicleStream::stream; // To List List<Vehicle> vehicles = supplier .get() .filter(vehicle -> vehicle.getBrand().equals("Audi")) .collect(Collectors.toList()); System.out.println(" Audi List"); System.out.println(vehicles); // To Set Set<String> vehicleSet = supplier.get().map(vehicle -> vehicle.getBrand()).collect(Collectors.toSet()); System.out.println(" Brand Set"); System.out.println(vehicleSet); // To Map Map<Long, Vehicle> vehicleMap = supplier .get() .filter(vehicle -> vehicle.getBrand().equals("Audi")) .collect(Collectors.toMap(Vehicle::getId, vehicle -> vehicle)); System.out.println("Audi Map"); System.out.println(vehicleMap); // Group by Map<String, List<Vehicle>> vehicleMap1 = supplier .get() .filter(vehicle -> vehicle.getBrand().equals("Audi")) .collect(Collectors.groupingBy(Vehicle::getYears)); System.out.println(" Group Map"); System.out.println(vehicleMap1); // Join Strings List<String> list = new ArrayList<>(); list.add("abc"); list.add("abc"); list.add("edf"); Stream<String> listStream = list.stream(); String newString = listStream.collect(Collectors.joining("-")); System.out.println(" New String "); System.out.println(newString); }
输出结果:
Audi List [StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null), StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=300000, years=2000, sale=null)] Brand Set [Audi, Mercedes-Benz, BMW] Audi Map {5=StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null), 6=StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=300000, years=2000, sale=null)} Group Map {2000=[StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=300000, years=2000, sale=null)], 2022=[StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null)]} New String abc-abc-edf
For Each示例
public void foreachEnd(List<Vehicle> vehicleStream) { Supplier<Stream<Vehicle>> supplier = vehicleStream::stream; // print all supplier .get() .filter(vehicle -> vehicle.getBrand().equals("Audi")) .forEach(vehicle -> System.out.println(vehicle)); // add 1000 to price / side affect supplier .get() .filter(vehicle -> vehicle.getBrand().equals("Audi")) .forEach(vehicle -> vehicle.setPrice(vehicle.getPrice().add(new BigDecimal("1000")))); // print again, price changed supplier .get() .filter(vehicle -> vehicle.getBrand().equals("Audi")) .forEach(vehicle -> System.out.println(vehicle)); }
输出结果:
StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null) StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=300000, years=2000, sale=null) StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1501000, years=2022, sale=null) StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=301000, years=2000, sale=null)
Count 示例
public void countEnd(List<Vehicle> vehicleStream) { Supplier<Stream<Vehicle>> supplier = vehicleStream::stream; // Count Long size = supplier.get().count(); System.out.println(size); // Cheapest one Vehicle vehicleMin = supplier .get() .min((vehicle1, vehicle2) -> vehicle1.getPrice().compareTo(vehicle2.getPrice())) .get(); System.out.println(vehicleMin); // Dearest One, with Comparator Vehicle vehicleMax = supplier.get().max(Comparator.comparing(Vehicle::getPrice)).get(); System.out.println(vehicleMax); }
输出结果:
6 StreamMemos.Vehicle(id=6, brand=Audi, wheels=4, type=a4, price=300000, years=2000, sale=null) StreamMemos.Vehicle(id=5, brand=Audi, wheels=4, type=rs7, price=1500000, years=2022, sale=null)
其他
- 注意:Stream的中间操作是惰性操作,只有当返回操作执行的时候,才会执行中间操作。所以虽然代码中声明了中间操作,会对元素的相关属性进行修改,但只有当返回操作执行的时候,相应的修改才会执行。
- 注意:因为Stream的操作对象通常是集合对象,当使用map等操作对集合内元素进行属性变化的时候,实际是对集合中的元素对象进行了操作,因此原始集合中的元素也会发生变化,必要时可通过创建新的对象来避免对原集合的修改。
- 注意:当Stream通过Supplier + generate方法生成无限大的流数据时,通常需要配合limit,findFirst,allMatch,anyMatch这一类具备short-circuiting能力的操作。
其他一些常用的操作:
- peek:中间操作,用于取出当前正在操作的元素,可以用于debug + System.out::print,快速查看具体每个元素的操作结果。peek本身接收的是一个consumer,不是function(区别于map),因此peek不具备返回值,因此正常情况不会对stream中的元素进行修改,但注意当元素是对象时,consumer中的修改会被传递。
- reduce:返回操作,类似collect,对Stream中的值进行函数操作/组合,最终形成归一操作,sum/max/count等实际是reduce操作的特殊版本。reduce本质上可以等价为for loop,但其可以基于Parallel Stream,用来实现并行操作。所以当需要对较大的集合进行for循环时,可以考虑使用reduce提高效率。实际使用的时候,reduce接受BinaryOperator作为执行器,第一个参数为局部结果,第二个参数为新的元素,并返回一个新的局部结果。如果用于并行运算,或者局部结果与输入元素存在类型差异(BiFunction)时,还需要提供reduce一个BinaryOperator(combiner)用于明确局部结果(执行器)的类型。通常可以使用的场景,累计运算,最大最小值。注意:并行运算使用reduce的时候,需要使用reduce的三参形式,其中第一个参数(0初始值)会参与所有并行流的计算。参考:https://www.baeldung.com/java-stream-reduce
2022-11 Ivan Dong
本文由 Ivan Dong 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Jun 13, 2023 at 10:09 am