JAVA 函数式编程学习之Stream API

在函数编程是在java 8中加入的新内容(还不知道java9就出来了),java 8之所以费这么大功夫引入函数式编程,原因有二:

  • 代码简洁,函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环。
  • 多核友好,Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下parallel()方法。

Stream的另外一个大特点是数据源本身可以是无限的,这在当今这个数据大爆炸的时代还量化的今天是非常必要的,有时候处数据不得不脱离RDBMS,或者以底层返回的数据为基础进行更上层的数据统计。

下面通过看《java 8函数式编程》后简单的总结下Stream API 的一些简单的认识和使用。由于stream中使用了大量的lambda表达式,如果对lambda还不太了解可以先看下这篇:http://blog.csdn.net/leoe_/article/details/78457368

函数式编程:在思考问题时,使用不可变值和函数,函数对一个值进行处理,映射成另一个值

在看Stream之前,先来看看迭代的使用:

从外部迭代到内部迭代

使用for循环进行计算人数:

int count = 0;
for(People people : allPeople){
    if(people.isFrom("China")){
        count++;
    }
}

for循环其实是一个封装了迭代的语法糖,工作的原理为,首先调用了iterator方法,产生了一个新的Iterator对象,进而控制整个迭代过程,就是外部迭代。迭代的过程通过显式调用Iterator对象的hasNext和next方法完成

int count = 0;
Iterator iterator = allPeople.iterator();
while(iterator.hasNext()){
    People people = iterator.next();
    if(people.isFrom(Chian)){
        count++;
    }
}

再来看看内部迭代的使用:

long coutn = allPeople.stream()
                      .filter(people -> people.isFrom("China"))
                      .count();

该方法返回内部迭代中相应接口:Stream

在内部迭代中整个过程被分解了两种更简单的操作:过滤和计数,Stream里的方法虽然都是java方法,但是返回的Stream对象却不是一个新集合,而是创建新集合的配方。

上面代码中的filter知识刻画了Stream并没有产生新的集合,叫做惰性求值方法,像count最终会从Stream产生的方法叫做及早求值方法,而判断一个操作是惰性求值还是及早求值很简单,只需要看它的返回值,如果返回值是Stream那么是惰性求值,如果返回值是一个值或者为空那么就是及早求值。

其中Stream API 的接口非常的多:

中间操作:concat() distinct() filter() flatMap() limit() map() peek() skip() sorted() parallel() sequential() unordered()
结果操作:allMatch() anyMatch() collect() count() findAny() findFirst() forEach() forEachOrdered() max() min() noneMatch() reduce() toArray()

stream的操作分为为两类,中间操作(intermediate operations)和结束操作(terminal operations),(这些概念在Spark中也出现过)二者特点是:

1. 中间操作总是会惰式执行,调用中间操作只会生成一个标记了该操作的新stream,仅此而已。
2. 结束操作会触发实际计算,计算发生时会把所有中间操作积攒的操作以pipeline的方式执行,这样可以减少迭代次数。计算完成之后stream就会失效

首先流的构成为下面三个基本步骤:

获取一个数据源(source)->数据转换 ->执行操作获取结果,每次转换原有Stream对象不改变,返回一个新的Stream对象。

Stream和函数接口关紧密结合,没有函数接口stream就无法工作,函数接口是指只有一个抽象方法的接口,通常函数接口出现的地方都可以使用Lambda表达式

常用的流操作:

collect(toList()):

collect(toList())方法由Stream里的值生成一个列表,是一个及早求值操作。

List<String> collected = Stream.of("a", "b", "c")                    //由列表生成一个Stream
                               .collect(Collectors.toList());    //在对stream进行操作后进行collect操作,由Stream生成列表

assertEquals(Arrays.asList("a", "b", "c"), collected);                  //使用断言判断结果是否和预期一致

这段代码展示了如何使用collect(toList())方法从Stream中生成一个列表,其中Stream的of方法使用一组初始值生成Stream。

map:

map是将一种类型的值转换成另外一中类型

使用map操作将字符串转化为大写形式

List<String> collected = Stream.of("a", "b", "hello")
                         .map(string -> string.toUpperCase())
                         .collect(toList());
asserEquals(asList("A", "B", "HELLO"), collected);

传给map Stream的Lambda表达式只接受一个String类型的参数,返回一个新的String,参数和返回值不必属于同一种类型,但是Lambda表达式必须是Function接口的一个实例,Function接口是只包含一个参数的普通函数接口。

filter:

下面要实现的是找出一组字符串中一数字开头的字符串,通常使用的办法都是运用for循环,内部使用if条件语句判断字符串的第一个字符来解决这个问题, 那么下面看看使用filter模式是如何实现

List beginningWithNumbers = Stream.of("a", "1asd", "asc2")
                                          .filter(value -> isDigit(value.charAt(0)))
                                          .collect(toList());

filter和map很像,filter接受一个函数作为参数,该函数用Lamdba表达式表示,经过过滤,将Lambda表达式为true的元素被保留下来,该表达式Lambda的函数接口正是Predicate接口。

flatMap:

flatMap方法可用Stream替换值,然后将多个Stream连接成一个Stream

下面是一个包含多个列表的流,希望得到所有数字的序列的实现:

List together = Stream.of(asList(1, 2), asList(3 ,4))
                               .flatMap(numbers -> numbers.stream())
                               .collect(toList());

调用stream方法,将每个列表转换成Stream对象,其余的部分由flatMap方法处理。

max and min:

下面是一个查找每个人身高的代码:

List peoples = asList(new People("will",192),
                              new People("lee",182));

People shortpeople = peoples.stream()
                      .min(Comparator.comparing(people -> people.getLength()))
                      .get();
assertEquals(tracks.get(1), shortpeople);

其中为了让Stream对象按照高低进行排序,需要传给它一个Comparator对象,comparing是java8中的静态方法,使用它可以方便的实现一个比较器。其中返回的对象为Optional对象中值。

reduce:

reduce操作可以实现从一组值中生成一个值,上面中的count、min、max方法都是reduce操作,因为经曾使用所以被纳入标准库中。

下面是使用reduce操作对Stream中的数字和。

int count = Stream.of(1 , 2, 3)
                  .reduce(0, (acc, element) -> acc + element); asserEquals(6, count);

上面Lambda表达式就是reduce,有两个参数,传入Stream中的当前元素和acc,然后将两个参数相加,acc是累加器,保留着当前的结果。

以上就是几个简单的stream API的介绍,要想在以后的编程中更好的应用函数式编程还需要进一步的学习和练习,下面是几个学习的网址

相关主题

  • Oracle Java 8 官方文档对 java.util.streampackage的说明。
  • 一篇教程:Java 8 Stream API and Functional Interfaces。
  • 关于Lambda 和 Stream 更多介绍的教程。

你可能感兴趣的:(java)