Java函数式编程之如何使用流Stream

发布时间:2021-10-20 10:26:11 作者:iii
来源:亿速云 阅读:179

这篇文章主要讲解了“Java函数式编程之如何使用流Stream”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java函数式编程之如何使用流Stream”吧!

 List<String> words = Arrays.asList("hello","world");
 List<String> chars = words.stream()
     .flatMap(word ->  Stream.of(word.split("")))
     .collect(Collectors.toList());

   Jdk8中新增的特性旨在帮助程序员写出更好的代码,其中对核心类库的改进是很关键的一部分。对核心类库的改进主要包括集合类的API和新引入的流(Stream)。流使程序员得以站在更高的抽象层次上对集合进行操作。

1.什么是流(Stream)?

Stream是特定类型的对象形成的一个队列并支持聚合操作。 Java中的Stream并不会存储元素,而是按需计算。流的来源可以是集合,数组,I/O channel, 产生器generator 等。聚合操作类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。和以前的Collection操作不同, Stream操作还有两个基础的特征:Pipelining中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道,如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
在 Java 8 中, 集合接口有两个方法来生成流:

List<String> list = Arrays.asList("a", "b", "c", "d", null);
List<String> filtered = list.stream().filter(e -> Objects.nonNull(e)).collect(Collectors.toList());
long count = list.parallelStream().filter(e -> Objects.nonNull(e)).count();

2.内部迭代

Java程序员在使用集合时,一个通用的模式时在集合上进行迭代,在迭代过程中处理每一个元素并返回处理结果。通常代码时这样的:

 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
 int sum = 0;
 Iterator<Integer> iterator = list.iterator();
 while (iterator.hasNext()) {
    sum += iterator.next();
 }
System.out.println(sum);

这段代码本身没什么问题就写起来有点麻烦,就其背后原理来看其实就是一个封装了迭代的语法糖,首先调用iterator方法产生一个新的Iterator对象进而控制整个迭代过程,这就是外部迭代。

然而外部迭代的问题在于首先,很难抽象出后续提到的复杂操作,另外从本质上来讲只是一种串行化操作。总体来看使用for循环会将行为和方法混为一谈。另一种方法是内部迭代,和调用iterator()作用一样使用stream()方法,该方法不是返回一个控制迭代的Iterator对象,而是返回内部迭代中的相应接口:Stream。示例如下:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().filter(e -> e > 5).reduce(0, Integer::sum);
System.out.println(sum);

3.实现机制

通常在Java 中调用一个方法计算机会随即执行操作:比如,System.out.println("hello world!")会在终端上输出一条信息。Stream里的一些方法略有不同,它们虽是普通Java方法但是返回的Stream对象并不是一个新的集合,而是创建新集合的配方。

list.stream().filter(e -> e > 5);

上述代码并未做什么实际性的工作,filter只是刻画出了Stream但是并没有产生新的集合,像filter这样只描述Stream,最终不产生新集合的方法惰性求值方法,而像sum()这样最终从Stream产出值的方法叫做及早求值方法。如果在filter方法中添加一条输出语句就会比较清楚的看出差异。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
list.stream().filter(e -> {
            System.out.println(e);
            return e > 5;
          });

如果将上述语句添加一个及早求值方法就可以看到集合中的元素被打印出来。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
list.stream().filter(e -> {
            System.out.println(e);
            return e > 5;
        }).findFirst();

判断一个操作是惰性求值还是及早求值很简单,只要返回值是Stream那么是惰性求值。使用这些操作的理想方式就是形成一个惰性求值链,最后用一个及早求值的操作返回想要的结果,这正是他的合理之处。那为什么要区分惰性求值和及早求值呢?原因很简单只需要一次迭代就可以得到全部结果,例如 :上述代码只需要找到第一个大于5的数而不需要比较所有集合元素。

4.常用的流操作

为了更好的学习Stream,我们接下来学习一些常用的stream操作。

如果需要在遍历集合元素时清洗非法元素可以使用filter方法,我们在上述讨论过程中已经使用过该方法。filter接收一个函数作为参数,该函数使用lambda表达式表示,如果被清洗元素符合预期则返回true,其实看源码会发现这个lambda表达式是我们之前讲过的 Predicate<T>函数。

这个方法是由Stream生成一个List,是一个及早求值操作。Jdk8中借鉴了很多Scala的设计思想,Stream的of方法可以用来生成一个新的Stream,我们来看下边的一个例子。

List<String> list = Stream.of("a", "b", "c","d","e").collect(Collectors.toList());

map函数是用来讲一个类型转换为另外一种类型,参数同样是一个lambda表达式对应我们之前讲过的 Function<T, R> 函数。因为map是一个惰性求值方法我们配合之前的collect看一个例子。

  List<String> list = Stream.of("a", "b", "c").map(e -> e.toUpperCase()).collect(Collectors.toList());
  System.out.println(list);

和map类似,不同的是其每个元素转换得到的是Stream对象,最终会把转换得到的多个Stream连接成一个Stream。

 List<String> words = Arrays.asList("hello","world");
 List<String> chars = words.stream()
     .flatMap(word ->  Stream.of(word.split("")))
     .collect(Collectors.toList());

上述代码我们可以得到输出结果 :[h, e, l, l, o, w, o, r, l, d],如果将flatMap换成map会发现我们得到了一个List<Stream<String>> ,大家感兴趣可以上手一试。

和之前讲的函数不同,这两个方法要求参数是一个比较器 Comparator<T> 

Integer min = Stream.of(1,2,3,4,5).min(Integer::compare).get();System.out.println(min);Integer max = Stream.of(1,2,3,4,5).max(Integer::compare).get();System.out.println(max);

该函数可以实现从一组值中生成一个值,在上述例子中用到的 sum、max、min本质上都是reduce操作。Stream的求和结果每一步都将Stream中的元素累加至accumulator,遍历至Stream中的最后一个元素时,accumulator的值就是所有元素的和。我们看如下例子

 Integer sum = Stream.of(1, 2, 3, 4, 5).reduce(0, (acc, e) -> acc + e);
 System.out.println(sum);

这段代码如果你对scala熟悉那么会很容易理解,acc作为累加器存储了中间计算结果,整个lambda函数本质上是我们之前讲过的BinaryOperator。

感谢各位的阅读,以上就是“Java函数式编程之如何使用流Stream”的内容了,经过本文的学习后,相信大家对Java函数式编程之如何使用流Stream这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

推荐阅读:
  1. Java中Stream流怎么实现合并操作
  2. 怎么在Java 8中创建Stream流

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java stream

上一篇:如何看待stusts2添加durid数据源的问题。

下一篇:Git的标签操作是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》