20221107 Stream 操作总结

参考资料

我的理解

Stream 操作需要重视方法的参数,注意入参名称的含义,其次是入参的泛型

lambda 表达式的接口,需要记住约定,常用接口名称的含义

笔记

流遵循了 【做什么而非怎么做】的原则

流操作的特点

  1. 流并不存储其元素

  2. 流的操作不会修改其数据源

  3. 流的操作是尽可能惰性执行的

操作流时的典型流程

  1. 创建

  2. 转换

  3. 终止

流操作

创建流

集合转换成流

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

下表列出了基本类型 intlongdouble 的 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

posted @ 2022-11-11 10:28  流星<。)#)))≦  阅读(52)  评论(0编辑  收藏  举报