Java流库详解(java.util.stream)

Java流库(java.util.stream)

流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据视图。通过使用流,我们可以说明想要完成什么任务,而不是说明如何去实现它。(这一点受益于lambda表达式)我们将操作的调度留给具体的实现去解决。

流大量的用到了lambda表达式(同根生于Java8)之后的篇幅将会说到lambda表达式

一、 流的特点

流的特点

  1. 流并不储存元素。这些元素可能存储在底层的集合中,或这是按需生成。
  2. 流的操作不会修改其数据源。例如,filter方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含被过滤掉的元素。
  3. 流的操作是尽可能惰性执行的。这意味着直至需要其结果时,操作才会执行。例如如果我们只想查找前5个长单词(长度大于x的String)而不是所有长单词,那么filter方法就会在匹配到第5个单词后停止过滤。因此,我们甚至可以创建无限流。

典型流程

  1. 创建一个流。
  2. 指定将初始流转换为其他流的中间操作,可能包含多个步骤。
  3. 应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作。从此之后,这个流就再也不能用了。

二、 流的创建

JavaAPI中有大量方法都可以产生流

API

  1. java.util.Stream<T> 8自身产生流的方法(需要通过现有流对象)

    • Stream<T> filter(Predicate<? super T> p)

      产生一个流,其中包含当前流中满足p的所有元素

      这里的Predicate<? super T> 非常灵性,保证了传入的p对象中的boolean test(T t);方法一定能接受流中的每个元素。因为保证了Predicate<T>中的T是Stream<T>的超类。

    • Long count()

      产生当前流中元素的数量。这是一个终止操作。(严格上不算创建)

      java.util.Collection 1.2default方法

    • default Stream<E> stream()

    • default Stream<E> parallelStream()

      产生当前集合中所有元素的顺序流或者并行流。

  2. java.util.Stream 8的一些静态方法

    • static <T> Stream<T> of(T... values)

      产生一个元素为给定值的流。

    • static <T> Stream<T> empty()

      产生一个不包含任何元素的流。

    • static <T> Stream<T> generate(Supplier<T> s)

      产生一个无限流,它的值是通过反复调用函数s而构建的。

    • static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)

      产生一个无限流,他的元素包含种子、在种子上调用f产生的值、在前一个元素上调用f产生的值,等等。

  3. java.util.Arrays 1.2的静态方法

    • static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive) 8

      产生一个流,他的元素是由数组中指定范围内的元素构成的。

  4. java.util.regex.Pattern 1.4的静态方法

    • Stream<String> splitAsStream(CharSequence input) 8

      产生一个流,它的元素是输入中由该模式界定的部分。

  5. java.nio.file.Files 7

    • static Stream<String> lines(Path path) 8

    • static Stream<String> lines(Path path, Charset cs) 8

      产生一个流,它的元素是只定文件中的行,该文件的字符集为UTF-8,或者为指定的字符集。

  6. java.util.function.Supplier<T> 8

    • T get()

      提供一个值。

三、 filter、map和flatMap方法

流的转换会产生一个新的流,它的元素派生自另一个流中的元素。

filter:filter转换会产生一个流,它的元素与某种条件相匹配。filter的引元是Predicate<T> ,即从T到boolean的函数。

map:通常,我们想要按照某种方式来转换流中的值,此时,可以使用map方法并传递执行该转换的函数。

flatMap:见名之意,这是map方法的变种,可以将所有结果生成(摊平)在一个流里。即如果是在map方法中返回Stream<Stream<T>>,可以使用flatMap得到结果Stream<T>

APIjava.util.stream.Stream 8

  • Stream<T> filter(Predicate<? super T> predicate)

    产生一个流,它包含当前流中所有满足断言条件的元素。

  • <R> Stream<R> map (Function<? super T,? extends R> mapper)

    产生一个流,它包含将mapper应用于当前流中所有元素产生的结果。

    注1:如果返回值是流,则返回流的流(准确来说返回R就是R的流)

    注2:R和T的意思。R为返回值,所以返回值需要是R的子类。T是入参(原有Stream<T>中的T类型),所以接受参数需要是T的超类

  • <R> Stream<R> flatMap (Function<? super T,? extends Stream<? extends R>> mapper)

    产生一个流,它是通过将mapper应用于当前流中所有元素所产生的结果连接到一起而获得的。

    注意:这里的每个结果都是一个流。

四、 抽取子流和连接流

调用stream.limit(n)会返回一个新的流,它在n个元素之后结束。(如果原来的流更短,那么就会在流结束时结束)。这个方法对于裁剪无限流的尺寸会显得特别有用。

例如:

Stream<Double> stream = Stream.generate(Math::random).limit(100L);
stream.forEach(System.out::println);
调用stream.skip(n)正好相反:它会丢弃前n个元素。

例如:取前200自然数,然后去掉前100个

Stream<Integer> stream = Stream.iterate(1, (n)->n+1);
stream = stream.limit(200).skip(100);
stream.forEach(System.out::println);
我们可以用Stream类的静态方法concat方法将两个流连接起来

例如:

Stream<? extends Object> result = Stream.concat(stream, stream2);
result.forEach(System.out::println);

注意:第一个流不应该是无限的,否则第二个流永远都不会得到处理的机会。

API java.util.stream.Stream 8

  • Stream<T> limit(Long maxSize)

    产生一个流,其中包含了当前流中最初的maxSize个元素。

  • Stream<T> skip(Long n)

    产生一个流,它的元素时当前流中除了前n个元素之外的所有元素。

  • static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

    产生一个流,它的元素时a的元素后面跟着b的元素。

五、 其他的流转换

一、 流的去重,distinct方法,它的元素是从原有流中产生的,即原来的元素按照同样的顺序剔除重复元素后产生的。这个流显然能够记住它已经看到的元素

二、 流的排序,有多种sorted方法的变体可用。其中一种用于操作Comparable元素的流,而另一种可以接受一个Comparator。

三、 peek方法,peek方法会产生一个流,它的元素与原来流中的元素相同,但是在每次获取一个元素时,都会调用一个函数(你传入peek的函数Consumer<T>类型)。

API java.util.stream.Stream 8

  • Stream<T> distinct()

    产生一个流,包含当前流中所有不同元素。

  • Stream<T> sorted()

  • Stream<T> sorted(Comparator<? super T> comparator)

    产生一个流,它的元素时当前流中的所有元素按照顺序排列的。第一个方法要求元素是实现了Comparable类的实例。

  • Stream<T> peek(Comsumer<? super T> action)

    产生一个流,它与当前流中的元素相同,在获取其中每个元素时,会见其传递给action。

六、 简单约简(重要)

约简是一种终结操作(terminal operation),它们会将流约简为可以在程序中使用的非流值

API java.util.stream.Stream 8

  • Optional<T> max(Comparator<? super T> comparator)

  • Optional<T> min(Comparator<? super T> comparator)

    分别产生这个流的最大元素和最小元素,使用由给定比较器定义的排序规则,如果这个流为空,会产生一个空的Optional对象。这些操作都是终结操作。

  • Optional<T> findFirst()

  • Optional<T> findFirst()

    分别产生这个流的第一个和任意一个元素,如果这个流为空,会产生一个空的Optional对象。这些操作都是中介操作。

  • boolean anyMatch(Predicate<? super T> predicate)

  • boolean allMatch(Predicate<? super T> predicate)

  • boolean noneMatch(Predicate<? super T> predicate)

    分别在这个流中任意元素、所有元素和没有任何元素匹配给定断言时返回true。这些操作都是终结操作。

七、 收集结果

API

  1. java.util.stream.BaseStream 8

    • Iterator<T> oteratpr()

      产生一个获取当前流中的各个元素的迭代器。这是一种终结操作。

  2. java.util.stream.Stream 8

    • void forEach(Consumer<? super T> action)

      在流的每个元素上调用action。这是一种终结操作。

    • Object[] toArray()

    • <A> A[] toArray(IntFunction<A[]> generator)

      产生一个对象数组,或者在将引用A[]::new传递给构造器时,返回一个A类型的数组。这些都是终结操作。

    • <R,A> R collect(Collector<? super T,A,R> collector)

      使用给定的收集器来收集当前流中的元素。Collectors类有用于多种收集齐的工厂方法。例如

      Set<Integer> set = stream.collect(Collectors.toSet());
      TreeSet<Integer> treeSet = stream.collect(Collectors.toCollection(TreeSet::new));
      // 第二种方法可以控制获得的集的种类
      
  3. java.util.stream.Collectors 8

    注:Collectors全部是static方法,建议使用静态导入

    • static <T> Collector<T,?,List<T>> toList()

    • static <T> Collector<T,?,Set<T>> toSet()

      产生一个将元素收集到列表或集中的收集器。

    • static <T,C extends Collection<T>> Collector<T,?,C> toCollection(Supplier<C> collectionFactory)

      产生一个将元素收集到任意集合中早的收集器。可以传递一个诸如TreeSet::new的构造引用。

    • static Collector<CharSequence,?,String> joining()

    • static Collector<CharSequence,?,String> joining(CharSequence delimiter)

    • static Collector<CharSequence,?,String> joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix)

      产生一个连接字符串的收集齐。分隔符会置于字符串之间,而第一个字符串之前可以有前缀,最后一个字符串之后可以有后缀。如果没有指定,那么它们都为空。

    • static <T> Collector<T,?,IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper)

    • static <T> Collector<T,?,LongSummaryStatistics> summarizingInt(ToLongFunction<? super T> mapper)

    • static <T> Collector<T,?,DoubleSummaryStatistics> summarizingInt(ToDoubleFunction<? super T> mapper)

      产生能够生成(Int|Long|Double)SummaryStatistics对象的收集齐,通过它可以获得将mapper应用于每个元素后产生结果的个数、总和、平均值、最大值和最小值。例如:

      Stream<Double> stream = Stream.generate(Math::random).limit(100L);
      
      DoubleSummaryStatistics summary = stream.collect(Collectors.summarizingDouble((D)->D*10));
      summary.getAverage();
      summary.getCount();
      summary.getMax();
      summary.getMin();
      summary.getSum();
      
  4. IntSummaryStatistics 8

    LongSummaryStatistics 8

    DoubleSummaryStatistics 8

    • Long getCount()

      产生汇总后的元素个数。

    • (int|long|double) getSum()

    • double getAverage()

      产生汇总后元素的总和或平均值,或者在没有任何元素时返回0。

    • (int|long|double) getMax()

    • (int|long|double) getMin()

      产生汇总后的元素的最大值和最小值,或者在没有任何元素时,产生(Integer|Long|Double).(MAX|MIN)_VALUE。

八、 收集到映射表中(Map)

API java.util.stream.Collectors 8

  • static<T,K,U,M extends Map<K,U>> Collector<T,?,M> toMap(Function<? supper T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)

  • static<T,K,U,M extends ConcurrentMap<K,U>> Collector<T,?,M> toConcurrentMap(Function<? supper T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)

    产生一个收集器,它会产生一个映射表(Map)或并发映射表(ConcurrentMap)。keyMapper和valueMapper函数会应用到每个收集到的元素上,从而在所产生的映射表中生成一个键/值项。默认情况下,当两个元素产生相同的键时,会抛出一个IllegalStateException异常。你可以提供一个mergeFunction来合并具有相同键的值。默认情况下,其结果是一个HashMap或ConcurrentHashMap。你可以提供一个mapSupplier,它会产生所期望的映射表实例。

    注,最后的两个参数(mergeFunction,mapSupplier)可以没有,API中也提供了没有这俩个参数的函。

九、 群组和分区

API java.util.stream.Collectors 8

  • static <T,K> Collector<T,?,Map<K,List<T>>> groupingBy(Function<? super T,? extends K> classifier)

  • static <T,K> Collector<T,?,ConcurrentMap<K,List<T>>> groupingByConcurrent(Function<? super T,? extends K> classifier)

    产生一个收集器,它会产生一个映射表或并发映射表,其键是将classifier应用于所有收集到的元素上所产生的结果,而值是由具有相同键的元素构成的一个个列表。

  • static <T> Collector<T,?,Map<Boolean,List<T>>> partitioningBy(Preditcate<? super T> predicate)

    产生一个收集器,它会产生一个映射表,其键是true/false,而值是由满足/不满足断言的元素构成的列表。

十、 下游收集器

Collectors提供了很多强大的功能,其中下游收集器可以让我们轻松(可能吧)地处理下游数据(Map<K,V>中的V)

作为下游收集器,可以直接在groupingBy方法中使用,例如:

Stream<Double> stream = Stream.generate(Math::random).limit(10000L).map((k)->k*10);
Map<Integer, Long> collect = stream.collect(Collectors.groupingBy((k)->{return k.intValue();},Collectors.counting()));
collect.forEach((k,v)->System.out.println("K:"+k+"  V:"+v));

API java.util.stream.Collectors 8

  • static <T> Collector<T,?,Long> counting()

    产生一个可以对收集到的元素进行计数的收集器。

  • static<T> Cikkectir<T,?,Integer> summingInt(ToIntFunction<? super T> mapper)

  • static<T> Cikkectir<T,?,Long> summingLong(ToLongFunction<? super T> mapper)

  • static<T> Cikkectir<T,?,Double> summingDouble(ToIntFunction<? super T> mapper)

    产生一个收集器,对将mapper应用到收集到的元素上之后产生的值计算总和。

    例如:

    Map<Integer, Integer> collect2 = stream.collect(Collectors.groupingBy((k)->{return k.intValue();},Collectors.summingInt((k)->k.intValue())));
    collect2.forEach((k,v)->System.out.println("K:"+k+"   V:"+v));
    // 这将产生以0-9分开的10各组,v为每个组中所有数的合
    
  • static <T> Collector<T,?,Optional<T>> maxBy(Comparator<? super T> comparator)

  • static <T> Collector<T,?,Optional<T>> minBy(Comparator<? super T> comparator)

    产生一个收集器,使用comparator指定的排序方法,计算收集到的元素中的最大值和最小值。

  • static <T,U,A,R> Collector<T,?,R> mapping(Function<? super T,? extends U> mapper, Collector<? super U,A,R> downstream)

    产生一个收集器,它会产生一个映射表,其键是将mapper应用到收集到的数据上而产生的,其值是使用downstream收集器收集到的具有相同键的元素。

十一、 约简操作

reduce方法是一种用于从流中计算某个值的通用机制,其最简单的形式将接受一个二元函数,并从前两个元素开始持续应用它。

注意:如果收集操作是并行的,reduce就不是线程安全的,小心使用。

API java.util.stream.Stream 8

  • 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)

    注意:如果reduce是并行流,那么reduce会产生多个结果,需要提供第二个函数(组合器)将结果合并。

    用给定的accumulator函数产生流中元素的积累总和。如果提供了幺元,那么第一个被累计的元素就是该幺元。如果提供了组合器,那么它可以用来将分别累计的各个部分整合成总和。例如:

    int result = words.reduce(0,(total,word)->total+word.length(), (total1, total2)->total1+total2);
    
  • <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner)

    将元素收集到类型R的结果中。在每个部分上,都会调用cupplier来提供初始结果,调用accumulator来交替的将元素添加到结果中,并调用combiner来整合两个结果。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • Java8 in action 没有共享的可变数据,将方法和函数即代码传递给其他方法的能力就是我们平常所说的函数式...
    铁牛很铁阅读 1,213评论 1 2
  • /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home...
    光剑书架上的书阅读 3,856评论 2 8
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,581评论 18 399
  • 美国食品药品监督管理局(FDA)于2018年1月9日已批准将Eisai公司的多重受体酪氨酸激酶抑制剂甲磺酸Lenv...
    亨利福特健康阅读 960评论 0 0