stream | 基础知识

一、什么是stream?

Stream是使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象
Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。Java中的Stream并不会存储元素,而是按需计算
  • 数据源流的来源。可以实集合,数组,I/Ochannel,生产器generator等
  • 聚合操作类似SQL语句一样的操作,比如filter,map,reduce,find,match,sorted等

和以前的Collection操作不同,Stream操作还有两个基础的特征:

  • Pipelining:中间操作都会返回流对象本身。这样多个操作可以串联成一个管道,如同流式风格(fluent style)。这样做可以对操作进行优化,比如延迟执行(laziness)和短路(short-circuiting)
  • 内部迭代:以前对集合遍历都是通过lterator或者For-Each的方式,显式的在集合外部进行迭代,这叫外部迭代。Stream提供了内部迭代的方式,通过访问者模式实现。

二、Stream的特点

  • 无存储:Stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等

  • 为函数式编程而生:对Stream的任何修改都不会修改背后的数据源,比如对Stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新Stream

  • 惰式执行:Stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行

  • 可消费性:Stream只能被"消费"一次,一旦遍历就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成

图上可见:获取一些带有颜色的球作为数据源,首先过滤掉红色的、把它们重构成随机的三角形。再过滤掉小的三角形,最后计算出剩余图形的周长

对于流的处理,主要有三种关键性操作:分别是流的创建操作、中间操作intermediate operation)以及最终操作(terminal operation)

三、创建操作

通过已有的集合来创建流(常见)
在Java 8中,除了增加了很多Stream相关的类以外,还对集合类自身做了增强,在其中增加了stream方法,可以将一个集合类转换为流

List<String> strings = Arrays.asList("stream", "stream1", "stream2", "stream3", "stream4");
Stream<String> stream = strings.stream();

以上,通过一个已有的List创建一个流。除此以外,还有一个parallelStream方法,可以为集合创建一个并行流(多线程方式,需要考虑线程安全问题)

通过Stream创建流
可以使用Stream类提供的方法,直接返回一个由指定元素组成的流。

  • of方法
Stream<String> stream = Stream.of("stream", "stream1", "stream2", "stream3", "stream4");

如以上代码,直接通过of方式,创建并返回一个Stream

  • generator方法(很少用)

四、Stream中间操作

Stream有很多重点操作,多个中间操作可以连接起来形成一个流水线,每一个中间操作就像流水线上的一个工人,每个工人都可以对流水进行加工,加工后得到结果还是一个流

常用中间操作列表

1.filter
filter方法用于通过设置的条件过滤出元素(用于过滤成一个新的流)。

    public static void main(String[] args) {
        List<String> strings = Arrays.asList("stream1", "", "stream2");
        strings.stream().filter(string -> !string.isEmpty()).forEach(System.out::println);       
    }

2.concat
concat方法将两个Stream连接在一起,合成一个Stream。若两个输入的Stream都是排序,则新Stream也是排序的;若输入的Stream中任何一个是并行,则新的Stream也是并行;若关闭新的Stream时,原来两个输入的Stream都将执行关闭处理

       .forEach(integer -> System.out.print(integer + "  "));
// 1  2  3  4  5  

3.map
map方法用于映射每个元素到对应的结果,如下例子:使用map输出了元素对应的平方数

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().map( i -> i*i).forEach(System.out::println);
//9,4,4,9,49,9,25

它的意思是传入一个类型,返回一个类型,就是你本来是String的流,可以转换成Student这个类的流,或者其他的形式的流。

4.flatMap
flatMap方法于map方法类似,都是将原Stream中的每一个元素通过函数转换,不同的是,该函数转换的对对象是一个Stream,也不会在创建一个新的Stream,而是将原Stream的元素取代为转换的Stream。如果转换的Stream。如果转换函数生产的Stream为null,应由空Stream取代。flatMap有三个对于原始类型的变种方法,分别是:flatMapToInt,flatMapToLong和flatMapToDouble

Stream.of(1, 2, 3)
    .flatMap(integer -> Stream.of(integer * 10))
    .forEach(System.out::println);
    // 10,20,30

5.peek
peek方法生产一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数,并且消费函数优先执行,如果不是很清楚,可以看看consumer函数就知道了

Stream.of(1, 2, 3, 4, 5)
        .peek(integer -> System.out.println("accept:" + integer))
        .forEach(System.out::println);
// accept:1
//  1
//  accept:2
//  2
//  accept:3
//  3
//  accept:4
//  4
//  accept:5
//  5

6.limit/skip
limit返回Stream的前面n个元素;skip则是跳过前n个元素。

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().limit(4).forEach(System.out::println);
//3,2,2,3
numbers.stream().skip(4).forEach(System.out::println);
//7,3,5

7.distinct
distinct主要用来去重

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().distinct().forEach(System.out::println);
//3,2,7,5

五、Stream最终操作

Stream的中间操作得到的结果还是一个Stream,那么如何把一个Stream转换成我们需要的类型?比如计算出流中元素的个数、将流转换成集合等。这就是需要最终操作

最终操作会消耗流,产生一个最终结果。也就是说,在最终操作之后,不能再次使用流,也不能使用任何一个中间操作,否则将抛出异常

常见的最终操作

1.forEach
Stream提供了方法'forEach'来迭代流中的每个数据

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

2.count
count用来统计流中的元素个数

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

System.out.println(numbers.stream().count());

3.collect
collect就是一个归约操作,可以接受各种做法作为参数,将流中的元素累积成一个汇总结果

   List<String> strings = Arrays.asList("Stream", "Stream大神", "Stream菜鸡");

        List<Integer> collect = strings.stream().filter(string -> string.length() <= 6).map(String::length).sorted().limit(2).distinct().collect(Collectors.toList());

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

推荐阅读更多精彩内容