20221107 Stream 操作总结
参考资料
我的理解
Stream 操作需要重视方法的参数,注意入参名称的含义,其次是入参的泛型
lambda 表达式的接口,需要记住约定,常用接口名称的含义
笔记
流遵循了 【做什么而非怎么做】的原则
流操作的特点
-
流并不存储其元素
-
流的操作不会修改其数据源
-
流的操作是尽可能惰性执行的
操作流时的典型流程
-
创建
-
转换
-
终止
流操作
创建流
集合转换成流
java.util.Collection#stream()
Collection#parallelStream()
数组转换成流
java.util.stream.Stream#of(T...)
java.util.Arrays#stream(T[])
不包含元素的流
Stream#empty()
无限流
Stream#generate(Supplier)
Stream#iterate(T seed, UnaryOperator)
转换流
intermediate
最常使用
-
filter:过滤
-
map:映射转换
-
flatMap:将流压平,映射转换
Stream#filter(Predicate);
Stream#map(Function<? super T, ? extends R> mapper);
Stream#flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
抽取子流和连接流
-
limit:限制元素个数
-
skip:跳过元素个数
-
concat:连接流,第一个流不应该是无限流
Stream#limit(long maxSize)
Stream#skip(long n)
Stream#concat(Stream<? extends T> a, Stream<? extends T> b)
其他
-
distinct:去重
-
sorted:排序
-
peek:对单个元素执行消费者方法
Stream#distinct()
Stream#sorted(Comparator<? super T> comparator)
Stream#peek(Consumer<? super T> action)
终止流
terminal
-
count
-
max
-
min
-
findFirst
-
findAny
-
anyMatch
-
allMatch
-
noneMatch
Stream#count()
Stream#max(Comparator<? super T> comparator)
Stream#min(Comparator<? super T> comparator)
Stream#findFirst()
Stream#findAny()
Stream#anyMatch(Predicate)
Stream#allMatch(Predicate)
Stream#noneMatch(Predicate)
其他方法
-
iterator:返回迭代器
-
forEach:遍历
-
forEachOrdered:有序遍历,对执行过并行操作的流
-
toArray:转换成数组
Stream#iterator()
Stream#forEach(Consumer<? super T> action)
Stream#forEachOrdered(Consumer<? super T> action)
Stream#toArray(IntFunction<A[]>)
collect 方法
收集,终止流操作
<R, A> R collect(Collector<? super T, A, R> collector);
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
重载二不依赖于 Collector 接口
StringBuilder sb = getStream().collect(StringBuilder::new, (result, i) -> result.append(i),
(result1, result2) -> result1.append(result2));
supplier 必须是可变的对象,不可以是 BigDecimal 这种每次操作都返回新的对象
Collector 接口
java.util.stream.Collector
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics();
}
-
T :归约操作的输入元素的类型
-
A :归约操作的可变累积类型(通常作为实现细节隐藏)
-
R :归约操作的返回结果类型
java.util.stream.Collectors
类提供了很多现成的 Collector
接口实现类
combiner 方法是处理并行流的合并方法
转集合
@Test
public void testCollect() {
List<Integer> list = getStream().collect(Collectors.toList());
Set<Integer> set = getStream().collect(Collectors.toSet());
TreeSet<Integer> treeSet = getStream().collect(Collectors.toCollection(TreeSet::new));
}
private Stream<Integer> getStream() {
return Stream.iterate(0, i -> i + 1).limit(10);
}
joining
String str = getStream().map(Object::toString).collect(Collectors.joining(","));
System.out.println(str);
// 0,1,2,3,4,5,6,7,8,9
统计(转数字)
counting
会产生收集到的元素的个数summing ( Int | Long | Double )
会接受一个函数作为引元,将该函数应用到下游元素中,并产生它们的和maxBy
,minBy
会接受一个比较器,并产生下游元素中的最大值和最小值mapping
方法会产生将函数应用到下游结果上的收集器,并将函数值传递给另一个收集中
IntSummaryStatistics statistics = getStream().collect(Collectors.summarizingInt(i -> i));
System.out.println(statistics);
// IntSummaryStatistics{count=10, sum=45, min=0, average=4.500000, max=9}
Integer sum = getStream().collect(Collectors.summingInt(i -> i));
System.out.println(sum);
// 45
转 Map
<T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)
<T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction)
<T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)
-
Function keyMapper - 生成键的映射函数
-
Function valueMapper - 产生值的映射函数
-
BinaryOperator mergeFunction - 一个合并函数,用于解决与同一键关联的值之间的冲突,如提供给 Map.merge(Object, Object, BiFunction)
-
Supplier mapSupplier - 一个函数,它返回一个新的空 Map,结果将被插入其中
Map<Integer, Integer> map = getStream().collect(Collectors.toMap(i -> i, i -> i * i));
System.out.println(map);
// {0=0, 1=1, 2=4, 3=9, 4=16, 5=25, 6=36, 7=49, 8=64, 9=81}
TreeMap<Integer, Integer> treeMap =
getStream().collect(
Collectors.toMap(i -> i % 3, i -> i, (oldValue, newValue) -> newValue, TreeMap::new));
System.out.println(treeMap);
// {0=9, 1=7, 2=8}=7, 2=8}
对于每一个 toMap
方法,都有一个等价的可以产生并发映射表的 toConcurrentMap
方法,单个并发映射表可以用于并行集合处理。当使用并行流时,共享的映射表比合并映射表要更高效。注意,元素不再是按照流中的顺序收集的,但是通常这不会有什么问题
群组( groupingBy
)
<T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier)
<T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
<T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream)
-
Function classifier - 将输入元素映射到键的分类器函数
-
Supplier mapFactory - 一个函数,当被调用时,会产生一个新的所需类型的空 Map
-
Collector downstream - 实现下游规约的收集器
Map<String, List<MyBean1>> map1 = getMyBean1Stream().collect(Collectors.groupingBy(MyBean1::getS1));
Map<String, Map<String, List<MyBean1>>> map2 = getMyBean1Stream().collect(
Collectors.groupingBy(MyBean1::getS1, Collectors.groupingBy(MyBean1::getS2)));
Map<String, Map<String, Map<String, List<MyBean1>>>> map3 = getMyBean1Stream().collect(
Collectors.groupingBy(MyBean1::getS1,
Collectors.groupingBy(MyBean1::getS2, Collectors.groupingBy(MyBean1::getS3))));
分区( partitioningBy
)
<T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate)
<T, D, A> Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate, Collector<? super T, A, D> downstream)
-
Predicate predicate - 用于对输入元素进行分类的断言
-
Collector downstream - 实现下游规约的收集器
下游处理器 downstream
下游处理器 默认是 Collectors#toList
映射(mapping)
Stream 方法:map 是转换流操作
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
Collectors 方法:
<T, U, A, R> Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper, Collector<? super U, A, R> downstream)
// toMap 转 Map
Map<String, String> map1 = getMyBean1Stream().collect(Collectors.toMap(MyBean1::getS1, MyBean1::getS2));
printMap(map1);
// mapping 不同于 toMap
List<String> list1 = getMyBean1Stream().collect(Collectors.mapping(MyBean1::getS1, Collectors.toList()));
// mapping 和 toMap 相似
List<String> list2 = getMyBean1Stream().map(MyBean1::getS1).collect(Collectors.toList());
约简操作( reduce
)
Stream 方法:reduce 是终止流操作
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
Collectors 方法:
<T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op)
<T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op)
<T, U> Collector<T, ?, U> reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> op)
Stream 的 reduce 方法,重载一、二和 Collectors 提供的重载一、二功能相同,重载三不同
Stream 的 reduce 方法重载三,增加了对并行流支持的 combiner
Collectors 的 reduce 方法重载三,增加了对类型转换支持的 mapper
基本类型流
流库中具有专门的类型 IntStream
、LongStream
、DoubleStream
,用来直接存储基本类型值,而无需使用包装器。如果想要存储 short
、char
、byte
、boolean
,可以使用 IntStream
,而对于 float
,可以使用 DoubleStream
-
mapToInt
-
mapToLong
-
mapToDouble
以及对应的 flatMap 方法
并行流
-
Stream.parallel
-
Collection.parallelStream
-
Collectors.groupByConcurrent
只要在终结方法执行时,流处于并行模式,那么所有的中间流操作都将被井行化
排序并不排斥高效的并行处理 例如,当计算 stream.map(fun)
时,流可以被划分为 n 部分,它们会被并行地处理。然后,结果将会按照顺序重新组装起来
当放弃排序需求时,有些操作可以被更有效地并行化。通过在流上调用 unordered
方法,就可以明确表示我们对排序不感兴趣。 Stream.distinct
就是从这种方式中获益的一种操作。在有序的流中, distinct
会保留所有相同元素中的第一个,这对并行化是 种阻碍,因为处理每个部分的线程在其之前的所有部分都被处理完之前,并不知道应该丢弃哪些元素。如果可以接受保留唯一元素中任意一个的做法,那么所有部分就可以并行地处理(使用共享的集来跟踪重复元素)
还可以通过放弃排序要求来提高 limit
方法的速度
Stream<String> sample = words.parallel().unordered().limit(n);
常用函数式接口
函数式接口 | 参数类型 | 返回类型 | 抽象方法名 | 描述 | 其他方法 |
---|---|---|---|---|---|
Runnable |
无 | void | run |
作为无参数或返回值的动作运行 | |
Supplier<T> |
无 | T | get |
提供一个 T 类型的值 | |
Consumer<T> |
T | void | accept |
处理 一个 T 类型的值 | andThen |
BiConsumer<T, U> |
T, U | void | accept |
处理 T 和 U 类型的值 | andThen |
Function<T, R> |
T | R | apply |
有一个 T 类型参数的函数 | compose , andThen , identity |
BiFunction<T, U, R> |
T, U | R | apply |
有 T 和 U 类型参数的函数 | andThen |
UnaryOperator<T> |
T | T | apply |
类型 T 上的一元操作符 | compose , andThen , identity |
BinaryOperator<T> |
T,T | T | apply |
类型 T 上的二元操作符 | andThen , maxBy , minBy |
Predicate<T> |
T | boolean | test |
布尔值函数 | and , or , negate , isEqual |
BiPredicate<T, U> |
T, U | boolean | test |
有两个参数的布尔值函数 | and , or , negate |
下表列出了基本类型 int
、 long
和 double
的 34 个可能的规范。 最好使用这些特殊化规范来减少自动装箱。
基本类型的函数式接口:
函数式接口 | 参数类型 | 返回类型 | 抽象方法名 |
---|---|---|---|
BooleanSupplier |
none | boolean | getAsBoolean |
(R)Supplier |
none | (P) | getAs(R) |
(R)Consumer |
(P) | void | accept |
Obj(R)Consumer<T> |
T, (P) | void | accept |
(R)Function<T> |
(P) | T | apply |
(R)To(S)Function |
(P) | (Q) | applyAs(S) |
To(R)Functioi<T> |
T | (P) | applyAs(R) |
To(R)BiFunction<T,U> |
T, U | (P) | applyAs(R) |
(R)UnaryOperator |
(P) | (P) | applyAs(R) |
(R)BinaryOperator |
(P), (P) | (P) | applyAs(R) |
(R)Pedicate |
(P) | boolean | test |
(P) ,(Q) 为 int
, long
, double
; (R) ,(S) 为 Int
, Long
, Double
示例
Function<String, Integer> function = s -> Integer.parseInt(s) + 10;
Integer int1 = function.apply("100");
System.out.println(int1); // 110
Function<String, String> function2 = s -> s.substring(0, 2);
Integer int2 = function.compose(function2).apply("100");
System.out.println(int2); // 20
// 报错,lambda表达式类型不匹配
// Integer int3 = function.compose(s -> s.substring(0, 2)).apply("100");
Comparator 接口
相关方法
Stream 方法:sorted 方法是中间操作;如果是对集合转成的流进行操作,不会影响到原来集合的排序;不带参数的 sorted 方法使用的是自然排序 Comparator.naturalOrder
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
Collectors 方法
<T> Collector<T, ?, Optional<T>> minBy (Comparator<? super T> comparator)
<T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator)
List 方法
default void sort(Comparator<? super E> c)
使用
Comparator
是 @FunctionalInterface
函数式接口
int compare(T o1, T o2);
直接返回排序器
-
naturalOrder - 自然排序
-
reverseOrder - 自然排序的倒序
对已有的排序器进行操作
-
nullsFirst - null 排在最前
-
nullsLast - null 排在最后
-
reversed - 排序器顺序反转
灵活根据需要实现排序器
-
comparing
-
thenComparing