日暮途远

日暮途远,涸辙难行;东隅已逝,桑榆非晚

Java8 Stream API介绍

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

Stream的创建

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

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

Stream的简单使用

Stream的使用分为两种类型:

  1. Intermediate,一个Stream可以调用0到多个Intermediate类型操作,每次调用会对Stream做一定的处理,返回一个新的Stream,这类操作都是惰性化的(lazy),就是说,并没有真正开始流的遍历。
    常用操作:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel
  2. Terminal,一个Stream只能执行一次terminal 操作,而且只能是最后一个操作,执行terminal操作之后,Stream就被消费掉了,并且产生一个结果。
    常用操作:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny

使用示例:

收集结果

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

第一个参数Supplier用于生成一个目标集合容器类型的实例;
函数BiConsumer<t,u>用于处理T和U这两个类型的数据,用在此处,第二个参数表示BiConsumer<r, ?=”” super=”” t=””>表示处理集合类型R以及集合元素类型T,即将T添加到R中;
相应的第三个参数BiConsumer<r, r=””>则表示将集合类型R和另一个R做合并操作;
实例如下:

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

  • 对象::实例方法
  • 类::静态方法
  • 类::实例方法

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

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

  • keyMapper函数用于从实例T中得到一个K类型的Map key;
  • valueMapper函数用于从实例T中得到一个U类型的Map value;
  • mergeFunction函数用于处理key冲突的情况,默认为throwingMerger(),抛出IllegalStateException异常;
  • mapSupplier函数用于生成一个Map实例;

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

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

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

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

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

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

  • counting方法返回所收集元素的总数;
  • summing方法会对元素求和;
  • maxBy和minBy会接受一个比较器,求最大值,最小值;
  • mapping函数会应用到downstream结果上,并需要和其他函数配合使用;

 

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

Optional类型

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

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

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

函数式接口

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

函数式接口

总结

  • Stream的处理总会在最后的Terminal操作才会真正执行;
  • 没有内部存储,也不能改变使用到的数据源,每次操作都会生成一个新的流;
  • 并行流使用fork/join 池来实现,对于非CPU密集型任务,需要谨慎使用;
  • 相对于循环遍历操作代码可读性更高;
点赞
  1. 相思枫雨说道:

    可惜公司还在1.6上挣扎啊,1.7的特性都没用明白 :surprised:

发表评论

电子邮件地址不会被公开。

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">