携手创作,共同成长!开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
1. 什么是Stream流?
stream是数据通道,用于操作数据源所生成的元素序列,它可以实现对集合、数组的复杂操作,进行简化开发。
2. Stream流的执行流程
2.1 Stream流生命周期
graph LR
no1("数据源<br/>(集合、数组等)") --> no2(Stream流获取) --> no3("Stream流中间操作<br/>(filter、map/flatMap等)") --> no4("Stream流终结操作<br/>(forEach、collect等)")
注意:Stream流中间的链式操作,对数据源的数据进行处理(例如过滤、排序等),直到执行终止操作作才执行(Stream流操作是延迟执行的)
3. Stream流的特点
- Stream流配合lambda表达式使用使代码更加简洁明了。
- 对集合、数组的流式操作效率更高。
- 延迟执行的特性,避免了多余的开销。
- 提供了并行流,可以满足程序多线程处理数据的能力。(谨慎使用)
4. Stream流的使用
4.1 Stream流的创建
- 数组和集合创建流
//数组(通过静态方法获取流)
String[] arr = new String[]{"A", "B"};
Stream<String> stream01 = Stream.of(arr);
Stream<String> stream02 = Arrays.stream(arr);
//集合(Collection体系集合:使用默认方法stream()生成流)
List<String> list = Arrays.asList(arr);
Stream<String> stream03 = list.stream();
Set<String> set = new HashSet<>();
Stream<String> stream04 = set.stream();
//Map集合(Map转成Set集合,间接的生成流)
Map<String,Object> map = new HashMap<>();
Stream<String> stream05 = map.keySet().stream();
Stream<Object> stream06 = map.values().stream();
Stream<Map.Entry<String, Object>> stream07 = map.entrySet().stream();
- 其他常用创建流方式
//Stream.generate()返回一个无穷序列无序流
Stream<Integer> generate = Stream.generate(new Random()::nextInt).limit(3);
System.out.println(generate.collect(Collectors.toList()));
//Stream.iterate()返回由迭代产生的无限的数据
Stream.iterate(0,x->x+1).limit(10).forEach(System.out::println);
//Pattern.splitAsStream()返回将字符串分隔成流
Pattern.compile(" ").splitAsStream("hello world").forEach(System.out::println);
//BufferedReader.lines()将每行内容转成流
BufferedReader reader = new BufferedReader(new FileReader("文件路径"));
Stream<String> lineStream = reader.lines();
//创建并行流
Stream<String> stream = list.parallelStream();
//创建一个空的stream
Stream<Integer> emptyStream = Stream.empty();
//类似取1-5之间的数字生成流(range不包含5,rangeClosed包含5)
IntStream.range(1, 5);
IntStream.rangeClosed(1, 5);
//LongStream
//DoubleStream
4.2 Stream流的中间方法
4.2.1 常用函数式接口
接口名 | 抽象方法 | 概述 |
---|---|---|
Function<T,R> | R apply(T) | 函数型接口:该接口接收到数据,操作完数据,返回新的数据 |
Predicate | boolean test(T t) | 断言型接口:对数据做出指定的判断,返回判断结果 |
Consumer | void accept(T t) | 消费型接口:消费一个参数数据 |
Supplier | T get() | 供给型接口:返回一个指定参数的值 |
- 学习Stream流之前需要对常用的函数式接口有一定的了解,才能看懂方法的具体的使用。
4.2.2 并行流、串行流、无序流
-
串行流:串行流是基于pipeline(管道)的,串行流上的操作在单独的一个线程中执行的。
-
并行流:并行流可以利用系统多cup特性,底层主要运用Fork/Join框架进行实现,fork根据cpu核数进行数据分块,join对各个fork进行合并。(存在线程安全性问题)
-
无序流:不会执行任何操作来显式地对流进行排序。它的作用是消除了流必须保持有序的约束,从而允许后续操作使用不必考虑排序的优化。
4.2.3 常用的中间方法及其案例
方法名 | 修饰符和类型 | 说明 |
---|---|---|
map | Stream map(Function<? super T, ? extends R> mapper) | 接收一个函数为参数,该函数应用到每个元素上,并将其映射成一个新元素(一对一) |
flatMap | Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) | 接收一个函数为参数,该函数应用到每个元素上,并将其映射成一个新元素(一对多) |
filter | Stream filter(Predicate<? super T> predicate) | 设置条件来过滤元素形成新的Stream流 |
distinct | Stream distinct() | 去除Stream流中重复的元素 |
sorted | Stream sorted(Comparator<? super T> comparator) | 指定对应的规则进行排序 |
peek | Stream peek(Consumer<? super T> action) | 接收一个函数为参数,用于查看该参数,但不会映射返回新元素,主要用于调试 |
limit | Stream limit(long maxSize) | 用于获取指定数量的流,配合skip跳过多少元素 可分页 |
skip | Stream skip(long n) | 用于指定跳过多少元素,配合limit使用,可分页 |
parallel | S parallel() | 基于调用流,返回并行流 |
sequential | S sequential() | 基于调用流,返回串行流 |
unordered | S unordered() | 基于调用流,返回无序流 |
concat | Stream concat(Stream<? extends T> a, Stream<? extends T> b) | 将两个Stream流合并为新的Stream流 |
//map
Arrays.stream(new String[]{"A", "B"}).map(String::toUpperCase).forEach(System.out::println);
//flatMap
Stream<List<String>> stream = Stream.of(
Arrays.asList("A"),
Arrays.asList("B", "C")
);
Stream<String> outputStream = stream.
flatMap((childList) -> childList.stream());
outputStream.forEach(System.out::println);
//filter
Stream.of("A","B").filter((str) -> {return "A".equals(str);}).forEach(System.out::println);
//distinct
Stream.of("A","B","A").distinct().forEach(System.out::println);
//sorted
Stream.of("A","B","A").sorted().forEach(System.out::println);
//peek
Stream.of("A","B","A").sorted().peek(System.out::println).count();
//limit
Stream.of("A","B","A").limit(1).forEach(System.out::println);
//skip
Stream.of("A","B","A").skip(1).forEach(System.out::println);
//concat
Stream.concat(Stream.of("A","B","A"),Stream.of("C","D","F")).forEach(System.out::println);
//parallel
Stream<String> parallel = Stream.of("A", "B", "A").parallel();
//sequential
Stream.of("A","B","A").sequential().forEach(System.out::println);
//unordered
IntStream.rangeClosed(1, 100).unordered().forEach(System.out::println);
4.3 Stream流的终结方法及其案例
- Stream流的惰性机制,只有执行终结方法时,才会执行之前的中间方法。
方法名 | 修饰符和类型 | 说明 |
---|---|---|
forEach | void forEach(Consumer<? super T> action) | 接收一个Lambda表达式,Stream流的每一个元素上执行该表达式 |
forEachOrdered | void forEachOrdered(Consumer<? super T> action) | 接收一个Lambda表达式,Stream流的每一个元素按照顺序上执行该表达式(并行流无法保证有序性) |
toArray | 1、Object[] toArray(); 2、<A> A[] toArray(IntFunction<A[]> generator); | 返回该包含该流的元素数组,可指定具体参数 |
reduce | 1、Optional reduce(BinaryOperator accumulator); 2、T reduce(T identity, BinaryOperator accumulator) 3、 U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator combiner) | 把Stream流中元素组合起来并返回,sum可以理解为特殊的reduce |
collect | 1、<R, A> R collect(Collector<? super T, A, R> collector) 2、 R collect(Supplier supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner) | 收集器,将Stream流中的元素根据接受的参数类型返回(具体参数参考下表) |
min | Optional min(Comparator<? super T> comparator) | 根据提供的Comparator返回此流的最小值 |
max | Optional max(Comparator<? super T> comparator) | 根据提供的Comparator返回此流的最大值 |
count | long count() | 返回流中元素数量 |
anyMatch | boolean anyMatch(Predicate<? super T> predicate) | 流中存在元素符合predicate规则,则返回true |
allMatch | boolean allMatch(Predicate<? super T> predicate) | 流中全部元素符合predicate规则,则返回true |
noneMatch | boolean noneMatch(Predicate<? super T> predicate) | 流中没有元素符合predicate规则,则返回true |
findFirst | Optional findFirst() | 返回满足条件的第一个元素 |
findAny | Optional findAny() | 返回满足条件的任意元素 |
iterator | Iterator iterator() | 返回迭代器 |
4.3.1 collect收集器常用参数
工厂方法 | 返回类型 | 说明 | |
---|---|---|---|
toCollection | Collection | 将Stream流中的元素收集到创建的集合中 | |
toList | List | 将Stream流中元素收集为List,默认为ArrayList | |
toSet | Set | 将Stream流中元素收集为Set,默认为HashSet,自动去重 | |
toMap | Map<K,T> | 通过对应的生成器方法生成对应key和value并返回至集合中 | |
toConcurrentMap | ConcurrentHashMap<K,T> | 与toMap差不多,返回线程安全的ConcurrentHashMap | |
counting | Long | 计算Stream流中元素个数 | |
minBy | Optional | 最小值的Optional,如果为空返回的是Optional.empty() | |
maxBy | Optional | 最大值的Optional,如果为空返回的是Optional.empty() | |
summingInt | Integer | 求和,返回类型为Integer | |
summingLong | Long | 求和,返回类型为Long | |
summingDouble | Double | 求和,返回类型为Double | |
averagingInt | Double | 平均值,返回类型Integer | |
averagingLong | Double | 平均值,返回类型Long | |
averagingDouble | Double | 平均值,返回类型Double | |
summarizingInt | IntSummaryStatistics | 汇总,返回包含元素个数、最大值、最小值、求和值、平均值,值类型为Integer | |
summarizingLong | LongSummaryStatistics | 汇总,返回包含元素个数、最大值、最小值、求和值、平均值,值类型为Long | |
summarizingDouble | DoubleSummaryStatistics | 汇总,返回包含元素个数、最大值、最小值、求和值、平均值,值类型为Double | |
joining | String | 连接流中每个元素的toString方法生成的字符串 | |
mapping | 指定类型 | 先对流中的每个元素进行映射,即类型转换,然后再将新元素给定的Collector进行合并返回 | |
collectingAndThen | 转换函数返回的类型 | 转换结束后,对其结果再进行处理返回 | |
groupingBy | Map<K,List> | 根据流中元素的某个值对流中的元素进行分组,并将属性值做为结果map的键 | |
groupingByConcurrent | ConcurrentHashMap<K,List> | 与groupingBy类似,返回ConcurrentHashMap | |
partitioningBy | Map<Boolean,List> | 将流中的元素按照给定的校验规则的结果分为两个部分,放到一个map中返回,map的键是Boolean类型,值为元素的列表List | |
reducing | 归纳返回类型 | 与reduce方法类似 |
4.3.2 终结方法案例
//forEach
Stream.of("A","B","A").forEach(System.out::println);
Stream.of("A","B","A").forEachOrdered(System.out::println);
//toArray
Object[] objects = Stream.of("A", "B", "A").toArray();
String[] arr = Stream.of("A", "B", "A").toArray(String[]::new);
//==========reduce==========
String reduce = Stream.of("A", "B", "C", "D").reduce("", String::concat);
// 求最小值
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
//求和
int sumValue01 = Stream.of(1, 2, 3, 4).reduce(0,Integer::sum);
//求和,sumValue = 10, 无起始值
int sumValue02 = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
//过滤,字符串连接,concat = "ace"
String concat = Stream.of("a", "B", "c", "D", "e", "F").
filter(x -> x.compareTo("Z") > 0).
reduce("", String::concat);
//========================
//=======collect==========
//toList
List<Integer> list = Stream.of(1, 2, 3, 4).collect(Collectors.toList());
//toSet
Set<Integer> set = Stream.of(1, 2, 3, 4).collect(Collectors.toSet());
//toCollection
ArrayList<Integer>arrayList=Stream.of(1,2,3,4).collect(Collectors.toCollection(ArrayList::new);
//toMap/ConcurrentHashMap
Map<Integer, Integer> map1 = Stream.of(1, 2, 3, 4, 5, 6)
.collect(Collectors.toMap(i -> i, i -> i));
Map<Integer, Integer> map2 = Stream.of(1, 2, 3, 4, 5, 5)
.collect(Collectors.toMap(i -> i, i -> i, (k1, k2) -> k1));
HashMap<Integer, Integer> map3 = Stream.of(1, 2, 3, 4, 5, 5)
.collect(Collectors.toMap(i -> i, i -> i, (k1, k2) -> k1, HashMap::new));
//counting
Long count = Stream.of(1, 2, 3, 4).collect(Collectors.counting());
//minBy/maxBy
Integer min = Stream.of(1, 2, 3, 4).collect(Collectors.minBy(Comparator.naturalOrder())).get();
Integer max = Stream.of(1, 2, 3, 4).collect(Collectors.maxBy(Comparator.naturalOrder())).get();
//summingInt/summingLong/summingDouble
Integer integerSum = Stream.of(1, 2, 3, 4).collect(Collectors.summingInt(i -> i));
Long longSum = Stream.of(1L, 2L, 3L, 4L).collect(Collectors.summingLong(i -> i));
Double doubleSum = Stream.of(1.1, 2.2, 3.3, 4.4).collect(Collectors.summingDouble(i -> i));
//averagingInt/averagingLong/averagingDouble
Double averagingInt = Stream.of(1, 2, 3, 4).collect(Collectors.averagingInt(i -> i));
Double averagingLong = Stream.of(1L, 2L, 3L, 4L).collect(Collectors.averagingLong(i -> i));
Double averagingDouble = Stream.of(1.1, 2.2, 3.3, 4.4).collect(Collectors.averagingDouble(i -> i));
//summarizingInt/summarizingLong/summarizingDouble
IntSummaryStatistics summarizingInt = Stream.of(1, 2, 3, 4).collect(Collectors.summarizingInt(i -> i));
LongSummaryStatistics summarizingLong = Stream.of(1, 2, 3, 4).collect(Collectors.summarizingLong(i -> i));
DoubleSummaryStatistics summarizingDouble = Stream.of(1, 2, 3, 4).collect(Collectors.summarizingDouble(i -> i));
//joining
String str = Stream.of("zs", "ls", "ww").collect(Collectors.joining("->"));
//mapping [列子:将对象里面的名字收集返回list]
Stream.of(userA,userB,userC).collect(Collectors.mapping(User::getName,Collectors.toList()));
//collectingAndThen [例子:将结果集转为大写]
Stream.of("zs","ls","ww").collect(Collectors.collectingAndThen(Collectors.joining(","),String::toUpperCase));
//groupingBy/groupingByConcurrent [例子:将字符串长度作为键,将该长度的字符串列表作为值]
Map<Integer, List<String>> groupingBy = Stream.of("zs", "ls", "ww").collect(Collectors.groupingBy(String::length));
//partitioningBy [例子:将字符串长度大于3的字符串,与其他字符串分开保存]
Map<Boolean,List<String>>partitioningBy=Stream.of("123","11","3232").collect(Collectors.partitioningBy(x -> x.length() > 3));
//reducing [列子:求和]
Optional<Integer> a = Stream.of(12, 13, 14).reduce((num1, num2) -> {
return num1 + num2;
});
//===================
//min 最小值
Stream.of(12, 13, 14).min(Integer::compareTo);
//max 最大值
Stream.of(12, 13, 14).max(Integer::compareTo);
//count 求元素个数
Stream.of(12, 13, 14).count();
//anyMatch
Stream.of(12, 13, 14).anyMatch(x -> x > 12);
//allMatch
Stream.of(12, 13, 14).allMatch(x -> x > 11);
//noneMatch
Stream.of(12, 13, 14).noneMatch(x -> x > 14);
//findFirst
Stream.of(12, 13, 14).filter(x -> x > 12).findFirst();
//findAny
Stream.of(12, 13, 14).filter(x -> x > 12).findAny();
//iterator
Iterator<Integer> iterator = Stream.of(12, 13, 14).iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}