java 8中的Stream API

JAVA ginotang 1396℃ 0评论

Stream API的作用

Stream(流) API(是java 8新增的功能,它和lambda表达式共同提供了函数式编程的接口。Stream用于操作数据源,通常是数组或者是集合。流本身不保存数据,但是流与流之间可以传输数据,传输数据的过程可能会对数据进行特定的操作,例如过滤、排序等。另外,Stream并不会改变源数据,对任何数据的操作结果都是返回一个新的Stream。

filter、map和reduce

filter、map和reduce是Stream提供的三个方法,Stream API之所以强大,正正就是因为这三种操作。它们都是通过把特定操作应用到数据之中,然后把结果返回到一个新的Stream之中。

filter

filter是一个过滤器方法,通过把过滤规则传递给filter,filter就会根据这个规则计算结果。filter可应用的规则非常多,任何你想到的规则基本上都可以被正确使用。例如去除数据中的重复数据、获取数据中的奇数值/偶数值等等。

map

假如你有一个整数类型的集合,你想把集合中的每一个元素都乘以2,那么map是你的首选。也就是说,map的作用是把一个特定的操作(乘以2)应用到集合中的每一个数据之中,最后返回一个新的Stream。

reduce

reduce是一个缩减操作,因为reduce的最后返回结果只包含一个值。reduce每一次计算都是处理两个操作数,直到所有数据处理完毕,得出最后的结果只有一个值。例如获取最大值/最小值。

Stream的两种操作和状态

两种操作:

  • 终端操作
  • 中间操作

所有只返回一个值的操作都是终端操作,所以,reduce是终端操作,包括max和min,forEach方法也是终端操作。终端操作会消费流,被消费后的流不能再使用,否则会抛出IllegalStateException异常。

中间操作不会消费流,而是返回一个新的Stream,因此,任何中间操作都支持链式写法。例如filter和map,它们都是中间操作。也就是说,如果一个方法的返回值是Stream类型的,那么它就一定是中间操作。

两种状态:

  • 有状态
  • 无状态

两种状态是针对中间操作来说的,对于终端操作影响不大。

有状态是指在处理每一个元素的过程中,这些元素是互相依赖的。例如排序,排序要通过互相对比才能得到结果,任何元素都不能独立于其他元素之外完成排序。

无状态和有状态相反,它们无须依赖数据中的其他元素就可以完成工作,例如判断奇数和偶数。

一种操作是否有状态,决定了这个操作是否可以在并行流中正确执行。顾名思义,并行流是一种并行操作,它可以最大限度地利用cpu的计算能力,从而提高程序的执行速度。如果一种操作是有状态的,那么把它放到并行流中计算,计算结果很大可能会出错。

Optional对象

前面说了,Stream有两种操作,其实这两种操作分别对应两种不同的返回值。中间操作返回的类型是Stream,而这里的Optional对象,则是终端操作的返回值类型。

Optional对象是为了处理返回值可能存在、也可能不存在的场合提供的,换句话说,Optional对象不一定包含值。

Optional对象的两个核心方法是isPresent()get():

  • isPresent()方法可判断对象中是否存在值,如果有值则返回true,否则返回false
  • get方法用来获取对象中的值,如果对象中不包含值,则抛出NoSuchElementException异常。

通常会先使用isPresent方法判断对象是否有值,然后才调用get方法获取对象中的值。

流的创建

java 8 中的Collection接口提供了stream和parallelStream方法,前者返回一个普通的流对象,而后者则返回一个并行流对象。从这个接口返回的流对象用于处理集合元素。

如果需要处理数组,那么使用Arrays类提供的stream方法。

使用流进行排序(sorted)

流提供了sorted方法,用于对集合元素进行排序。下面是一个简单的例子

没有参数的sorted方法使用的排序是由小到大的排序,如果想倒序排序,可以往sorted方法传入一个比较器

上面的lambda表达式可以使用方法引用替换,使代码更加简介

由于sorted方法是有状态的,因此,不能把它放到并行流中处理数据。下面的代码排序结果是错误的,且每次运行的结果都不一样

使用map

map可以对每一个元素应用同样的操作,例如想把集合中所有字符串的空格去掉,可以这样做:

lambda表达式替换成方法引用

由于map是一个中间操作,因此可以结合其他中间操作如filter一起处理数据:

找出值为w开头的字符串并在字符串后面添加另一个字符串值:

使用reduce

reduce作用于集合,每次处理集合中的两个元素,直到所有元素处理完毕,最简单的例子就是计算集合元素的和。

可用ifPresent()方法替换isPresent()方法,前者代码更加简介

reduce内部是一个迭代操作,每次迭代的计算结果会保存到lambda的第一个参数之中,这里是a。

reduce还有两个参数的版本,这个版本会从第一个参数开始计算集合的值

输出结果:

流转换为集合(collect)

通常情况下,我们都是通过集合来获取流,然后使用流操作集合中的数据,但是,有时候可能会反过来,把流中的数据转换为集合。好在,流提供了collect方法来完成这个转换,使过程变得简单。

collect方法需要配合Collectors接口一起工作,因为collect方法需要接受一个Collector对象为参数,而Collectors接口则提供了一些静态方法来获取Collector对象,常用的有toList和toSet方法。前者把流转换为List,后者把流转换为Set。

collect方法还有另一个带三个参数的版本,这个版本可以对我们的转换过程做更多的控制,比如把数据装到什么类型的集合中,如何装。其中第三个参数是一个combiner,只有在使用parallelStream的时候才有用,它指定了多个线程的合并规则。

上面的代码指定了目标集合为ListedList类型,并把数据正常装入链表中。当然,装载数据的过程我们可以对数据进行过滤:

实际上,如果单纯从限制目标类型的角度来看,Collectors接口已经提供了方法简化我们的工作:

 

 

转载请注明:Pure nonsense » java 8中的Stream API

喜欢 (0)
0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x
()
x