Java8 Stream API

Stream API是Java8中处理集合的关键组件,提供了各种丰富的函数式操作。

Stream的创建

任何集合都可以转换为Stream:

//数组

String[] strArr =newString[]{"aa","bb","cc"};

Stream streamArr = Stream.of(strArr);

Stream streamArr2 = Arrays.stream(strArr);

//集合

List list =newArrayList<>();

Stream streamList = list.stream();

Stream streamList2 = list.parallelStream();

//并行执行...//generator 生成无限长度的

streamStream.generate(Math::random);

// iterate 也是生成无限长度的Stream,其元素的生成是重复对给定的种子值调用函数来生成的Stream.iterate(1, item -> item +1)

Stream的简单使用

Stream的使用分为两种类型:

Intermediate,一个Stream可以调用0到多个Intermediate类型操作,每次调用会对Stream做一定的处理,返回一个新的Stream,这类操作都是惰性化的(lazy),就是说,并没有真正开始流的遍历。

常用操作:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel

Terminal,一个Stream只能执行一次terminal 操作,而且只能是最后一个操作,执行terminal操作之后,Stream就被消费掉了,并且产生一个结果。

常用操作:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny

使用示例:

//filter 过滤操作

streamArr.filter(str->str.startsWith("a"));

//map 遍历和转换操作

streamArr.map(String::toLowerCase);

//flatMap 将流展开

Listlist1=newArrayList<>();

list1.add("aa");list1.add("bb");

Listlist2=newArrayList<>();

list2.add("cc");

list2.add("dd");

Stream.of(list1,list2).flatMap(str->str.stream()).collect(Collectors.toList());

//limit 提取子流

streamArr.limit(1);

//skip 跳过

streamArr.skip(1);

//peek 产生相同的流,支持每个元素调用一个函数

streamArr.peek(str->System.out.println("item:"+str));

//distinct 去重

Stream.of("aa","bb","aa").distinct();

//sorted 排序Stream.of("aaa","bb","c").sorted(Comparator.comparing(String::length).reversed());

//parallel 转为并行流,谨慎使用

streamArr.parallel();

//forEach

streamArr.forEach(System.out::println);

//forEachOrdered 如果希望顺序执行并行流,请使用该方法streamArr.parallel().forEachOrdered(System.out::println);

//toArray 收集到数组中

streamArr.filter(str->str.startsWith("a")).toArray(String[]::new);

//reduce 聚合操作

streamArr.reduce((str1,str2) -> str1+str2);

//collect收集到List中

streamArr.collect(Collectors.toList());

//collect收集到Set中

streamArr.collect(Collectors.toSet());

//min取最小值

IntStream.of(1,2,3,4).min();

Stream.of(arr).min(String::compareTo);

//max取最大值

IntStream.of(1,2,3,4).max();

Stream.of(arr).max(String::compareTo);

//count计算总量

streamArr.count();

//anyMatch判断流中是否含有匹配元素

boolean hasMatch = streamArr.anyMatch(str -> str.startsWith("a"));

//allMatch判断流中是否全部匹配

boolean hasMatch = streamArr.allMatch(str -> str.startsWith("a"));

//noneMatch判断流中是否全部不匹配

boolean hasMatch = streamArr.noneMatch(str -> str.startsWith("a"));

//findFirst找到第一个就返回

streamArr.filter(str -> str.startsWith("a")).findFirst();

//findAny找到任意一个就返回

streamArr.filter(str -> str.startsWith("a")).findAny();

收集结果

collect操作主要用于将stream中的元素收集到一个集合容器中,collect函数的定义如下:

R collect(Suppliersupplier,

BiConsumeraccumulator,

BiConsumercombiner);


第一个参数Supplier用于生成一个目标集合容器类型的实例;

函数BiConsumer

Set result = Stream.of("aa","bb","cc","aa").collect(

       () -> newHashSet(),

       (set, item)->set.add(item),

       (set, subSet)->set.addAll(subSet));

以上写法可以使用操作符“::”简化,语法如下:

对象::实例方法

类::静态方法

类::实例方法

Set result=Stream.of("aa","bb","cc","aa").collect( 

            HashSet::new,    

            HashSet::add,  

            HashSet::addAll);

Java.util.stream.Collectors类中已经预定义好了toList,toSet,toMap,toCollection等方便使用的方法,所以以上代码还可以简化如下:

Set result2 = Stream.of("aa","bb","cc","aa").collect(Collectors.toSet());

将结果收集到Map中,Collectors.toMap方法的两个重载定义如下:

keyMapper函数用于从实例T中得到一个K类型的Map key;

valueMapper函数用于从实例T中得到一个U类型的Map value;

mergeFunction函数用于处理key冲突的情况,默认为throwingMerger(),抛出IllegalStateException异常;

mapSupplier函数用于生成一个Map实例;

publicstatic    Collector> toMap(Function keyMapper,         

       Function valueMapper) {returntoMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);    }

publicstatic>    Collector toMap(Function keyMapper,                         

       Function valueMapper,                           

     BinaryOperator mergeFunction,                            

    Supplier mapSupplier) {    

    BiConsumer accumulator                = (map, element) -> map.merge(keyMapper.apply(element),                                              valueMapper.apply(element), mergeFunction);

returnnewCollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);  

  }

假设有一个User实体类,有方法getId(),getName(),getAge()等方法,现在想要将User类型的流收集到一个Map中,示例如下:

Stream userStream=Stream.of(newUser(0,"张三",18),newUser(1,"张四",19),newUser(2,"张五",19),newUser(3,"老张",50));

Map userMap=userSteam.collect(Collectors.toMap(User::getId, item->item));

假设要得到按年龄分组的Map>,可以按这样写:

Map>ageMap=userStream.collect(Collectors.toMap(User::getAge, Collections::singletonList, (a, b)->{ListresultList=newArrayList<>(a);            resultList.addAll(b);returnresultList;        }));

这种写法虽然可以实现分组功能,但是太过繁琐,好在Collectors中提供了groupingBy方法,可以用来实现该功能,简化后写法如下:

Map>ageMap2=userStream.collect(Collectors.groupingBy(User::getAge));

1

1

类似的,Collectors中还提供了partitioningBy方法,接受一个Predicate函数,该函数返回boolean值,用于将内容分为两组。假设User实体中包含性别信息getSex(),可以按如下写法将userStream按性别分组:

Map> sexMap = userStream.collect(Collectors.partitioningBy(item -> item.getSex() >0));

1

1

Collectors中还提供了一些对分组后的元素进行downStream处理的方法:

counting方法返回所收集元素的总数;

summing方法会对元素求和;

maxBy和minBy会接受一个比较器,求最大值,最小值;

mapping函数会应用到downstream结果上,并需要和其他函数配合使用;

MapsexCount=userStream.collect(Collectors.groupingBy(User::getSex,Collectors.counting()));MapageCount=userStream.collect(Collectors.groupingBy(User::getSex,Collectors.summingInt(User::getAge)));Map>ageMax=userStream.collect(Collectors.groupingBy(User::getSex,Collectors.maxBy(Comparator.comparing(User::getAge))));Map>nameMap=userStream.collect(Collectors.groupingBy(User::getSex,Collectors.mapping(User::getName,Collectors.toList())));

1

2

3

4

5

6

7

1

2

3

4

5

6

7

以上为各种collectors操作的使用案例。

Optional类型

Optional 是对T类型对象的封装,它不会返回null,因而使用起来更加安全。

ifPresent方法接受一个函数作为形参,如果存在当前Optinal存在值则使用当前值调用函数,否则不做任何操作,示例如下:

Optional optional =...optional.ifPresent(v -> results.add(v));

1

2

1

2

orElse方法,orElseGet方法,当值不存在时产生一个替代值,示例如下:

Stringresult =optional.orElse("defaultValue");Stringresult =optional.orElseGet(() -> getDefalutValue());

1

2

1

2

可以使用Optional.of()方法和Optional.empty()方法来创建一个Optional类型对象,示例如下:

a - b >0?Optional.of(a - b) :Optional.empty();

1

1

函数式接口

Steam.filter方法接受一个Predicate函数作为入参,该函数返回一个boolean类型,下图为Stream和COllectors方法参数的函数式接口:

总结

Stream的处理总会在最后的Terminal操作才会真正执行;

没有内部存储,也不能改变使用到的数据源,每次操作都会生成一个新的流;

并行流使用fork/join 池来实现,对于非CPU密集型任务,需要谨慎使用;

相对于循环遍历操作代码可读性更高;

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

推荐阅读更多精彩内容

  • 了解Stream ​ Java8中有两个最为重要的改变,一个是Lambda表达式,另一个就是Stream AP...
    龙历旗阅读 3,298评论 3 4
  • pyspark.sql模块 模块上下文 Spark SQL和DataFrames的重要类: pyspark.sql...
    mpro阅读 9,438评论 0 13
  • Int Double Long 设置特定的stream类型, 提高性能,增加特定的函数 无存储。stream不是一...
    patrick002阅读 1,267评论 0 0
  • Java8 Stream-API Stream 流类似集合类中的Iterator,但是比Iterator高级,只需...
    Mrsimple_4f84阅读 474评论 0 1
  • 先看一段代码 Stream API的历史 在java8引入 受益于lambda表达式 lambda表达式 接口常被...
    ThomasYoungK阅读 624评论 0 0