Java8-流-用流收集数据和并行流

下载.png

收集器简介 汇总 并行流

欢迎访问本人博客查看原文:http://wangnan.tech

收集器简介

对流调用collect方法将对流中的元素触发一个规约操作

Collectors实用类提供了很多静态工程方法,可以方便地创建常用收集器的实例,只要拿来用就可以了

最常用的是toList方法,它会把流收集到一个List中:

List<Transcation> transactions = transcationStream.collect(Collectors.toList());

预定义收集器

Collectors类提供的工厂方法(例如groupingBy)创建的收集器,他们主要提供了三大功能:

  • 将流元素规约和汇总为一个值
  • 元素分组
  • 元素分区

规约和汇总

counting方法计算菜单里有多少种菜

long howManyDishes = menu.stream().collect(Collectors.counting());

这还可写的更为直接:

long howMangDishes = menu.stream().count()

假如你已经导入了Collectors类的所有静态工厂方法,你就可以写counting()而不是Collectors.counting()

假如你想要找到菜单中的热量最高的菜,你可以使用两个收集器,Collectors.maxBy和Collextors.minBy,来计算流中的最大和最小值。

comparator<Dish> disCaloriesComparator = Comparator.comparing(Dish::getCalories);

Optional<Dish> mostCalorieDish = menu.stream().collect(maxby(dishCaloriesComparator));

汇总

有时候我们想一次操作就获得,最大值最小值总和与平均值,通过summarizing操作你就可以数出菜单中的元素个数,总和,平均值、最大值和最小值

IntSummaryStatistics menuStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));

IntSummaryStatistics类提供了getter方法来访问每个结果

连接字符串

joining工厂方法返回的收集器会把对流中每个对象应用toString方法得到的所有字符串连接成一个字符串

String shortMenu = menu.stream().map(Dish::getName).collect(joining());

如果Dish类有一个toString方法来返回菜肴的名称,那你无需用提取每一道菜的名称的函数可以得到相同结果:

String shortMenu = menu.stream().collect(joining());

字符串可读性不是很好,幸好,joining工厂有一个重载版本可以接受元素之间的分界符,这样就可以得到一个逗号分隔的菜肴名称列表

String shortMenu = menu.stream().collect(joining(", "));

广义的规约汇总

前面我们说的所有规约过程,其实都是Collects.reducing工厂方法提供的更广义规约收集器的特殊情况

Collects.reducing工厂方法是所有这些特殊情况的一般化,可以说先去的案例仅仅是为了方便程序员而已,例如,reduceing方法创建的收集器来计算你菜单的总热量,如下

int totalCalories = menu.stream().collect(reducing()0,Dish::getCalories,(i,j)->i+j);

它需要三个参数:

  • 第一个参数是规约的起始值
  • 第二个参数就是使用的函数
  • 但三个参数是一个BinaryOperator,将两个项目积累成同一类型的值

分组

Collectors.groupingBy工厂方法返回的收集器可以完成这项任务

Map<Dish.Type,List<Dish> dishesByType = menu.stream().collect(groupingBy(Dish:getType))

如果要按照这两个标准分类怎么办呢?
可以把一个内层groupingBy传递给外层groupingBy
例如:
groupingBy(a,b) a是第一级的条件,b是一个groupingBy

当然第二个参数也可以是一个条件,比如要数一数菜单中每类菜有多少个,可以传递counting收集器作为groupingBy收集器的第二个参数

Map<Dish.Type,Long> typesCount = menu.stream().collect(
                                groupingBy(Dish::getType,counting())
);

分区

分区是分组的特殊情况,由一个谓词作为分类函数,Map的键类型时boolean,一共两组,ture和false
例如:

Map<Boolean,List<Dish>> partitionedMenu = menu.stream().collect(partitionBy(Dish::isVegrtarian))

总结下Collectors类的静态方法

并行流

将顺序流转换为并行流

只需要调用parallel()

另外,你只需要对并行流调用sequential方法就可以把它变成顺序流

配置并行流线程池

并行流的线程从那里来?有多少个?怎么定义的?
回答:并行流内部使用了默认的ForkJoinPool,它默认的线程数量就是你的处理器数量,这个值是由Runtime.getRuntime().avaliableProcessors()得到的

改变线程池大小:

System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","12")

不建议修改

并行化并不是没有代价,并行化过程本身需要对流做递归划分,把每个子流的归纳分配到不同的线程,然后把这些操作的结果合并成一个值,但在多个内核之间移动数据的代价可可能比你想象的大,所以很重要的一点是要保证在内核中并行执行工作的时间比在内核之间传递数据的时间长

总而言之,很多情况下不可能或不方便并行化,然而,在使用stream之前,你必须确保用的对。

还有一些进阶内容 如分支/合并框架、工作窃取,Spliterator,本文不作介绍

总结

  • collect是一个终端操作,他接受的参数是将流中的元素积累到汇总的各种方式
  • 预定义收集器包括将流元素规约和汇总到一个值,例如计算最小值,最大值或平均值
  • 预定义收集器可以用groupingBy对流中元素进行分组,或用partitoningBy进行分区
  • 收集器可以高效的复合起来,进行多级分组,分区和规约
  • 你可以实现Collector接口中定义的方法来开发你自己的收集器
  • 内部迭代让你可以并行处理一个流,而无需再代码中显式使用和协调不同的线程
  • 虽然并行处理一个流很容易,却不能保证程序在所有情况下都运行的更快
  • 从性能角度来说,使用正确的数据结构,如尽可能利用原始流而不是一般化的流

(注:内容整理自《Java8实战》)

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

推荐阅读更多精彩内容