java8实战——自定义流的构建

前言

我们都知道java8的流操作带来许多便捷,但是某些情况下,某些类型变量无法直接生成流,但我们又希望能够拥有流的便捷操作解决问题,这又该怎么办呢?所以我们就用这篇博客来记录一下这个问题的解决方案

由值创建流

我们现在有几个字符串,我们希望将这一个个变量转为大写在输出,想使用流操作又该怎么办呢?
对于流操作很熟悉的你一定想着,我们可以把这一个个字符串扔到一个list中,在使用stream进行操作不就好了,所以我们得出这样一段代码

 Arrays.asList("Java 8", "Lambdas", "In", "Action")
 .stream()
 .map(String::toUpperCase)
 .forEach(System.out::println);

问题虽然解决,但是走了没必要的集合创建的步骤,实际上我们完完全全可以将几个字符串直接扔到流中,代码如下所示,可以看出使用Stream.of就可以完成将任意数量的字符串搞到流里面从而完成流操作

  Stream stream = Stream.of("Java 8", "Lambdas", "In", "Action");
        stream.map(String::toUpperCase).forEach(System.out::println);

当然,如果你想拿到一个空流的话,也可以使用下面这段代码

Stream emptyStream = Stream.empty();

由数组创建流

那么数组又该怎么办呢?如果我们现在有一个整型数据,希望它也能够使用流完成流程的总和计算又该怎么做呢?
你可能会想到将整型数组转成list再用stream进行计算,实际上我们完全可以将数组直接转为IntStream完成计算

 int[] numbers = {2, 3, 5, 7, 11, 13};
        System.out.println(Arrays.stream(numbers).sum());

由文件生成流

我们现在有一个txt文件,内容如下,我们希望统计出不重复的单词数

The quick brown fox jumped over the lazy dog
The lazy dog jumped over the quick brown fox

我们也完完全全可以为文件生成一个文件流,代码如下所示,使用Files.lines将对应路径的文件内容构成一个几行Stream,为了能够实现去重判断,我们需要将每一个的字符串使用空格分开所以我们在下一个流操作中使用line -> Arrays.stream(line.split(" ")),注意笔者完成这个操作的时候使用的是flatMap而不是map,假如我们使用map最终结果只会是2,原因很简单,txt文件就两行,假如我们使用map就会将 Files.lines生成的两个流分别进行split操作,从而得到两个带数组的流,最终去重结果自然是2。
所以我们使用flatMap,就是将这个两个流的内容合并到一起,完成一种所谓扁平化的操作,具体工作原理参见下图

     long uniqueWords = Files.lines(Paths.get("F://github//Java8InAction//src//main//resources//lambdasinaction//chap5//data.txt"), Charset.defaultCharset())
                 //将一行行单词构成的数组扁平化成一个流
                                 .flatMap(line -> Arrays.stream(line.split(" ")))
                 //去重
                                 .distinct()
                 //统计
                                 .count();

使用map的工作原理图

java8实战——自定义流的构建_第1张图片使用flatMap工作原理图
java8实战——自定义流的构建_第2张图片

由函数生成流,创建无限流

一个简单的示例

有时候我们希望能够任意指定检索数据的范围,那么无限流就是最佳的解决方案,代码如下所示,可以看到通过Stream.iterat就可以完成0开始的所有偶数的流构建

        Stream.iterate(0, n -> n + 2)
              .limit(10)
              .forEach(System.out::println);

两个方案解决斐波那契数列

使用 iterate

斐波纳契数列是著名的经典编程练习。下面这个数列就是斐波纳契数列的一部分:0, 1, 1,
2, 3, 5, 8, 13, 21, 34, 55…数列中开始的两个数字是0和1,后续的每个数字都是前两个数字之和。
斐波纳契元组序列与此类似,是数列中数字和其后续数字组成的元组构成的序列:(0, 1),
(1, 1), (1, 2), (2, 3), (3, 5), (5, 8), (8, 13), (13, 21) …
你的任务是用 iterate 方法生成斐波纳契元组序列中的前20个元素。

问题很简单,抓住规律每个数组的第一个数是上一个数组的第2个数,每个数组的第二个数是上一个数组的和,所以编码如下

        Stream.iterate(new int[]{0, 1}, n -> new int[]{n[1], n[0] + n[1]})
                .limit(10)
                .forEach(t -> System.out.println(String.format("( %d ,%d )", t[0], t[1])));

使用generate

generate使用上相较于iterate唯一的优势就是它是有状态的,什么是有状态的呢?说白了就是可以记录上一次迭代的结果,所以假如我们需要遍历斐波那契,我们的代码可以编写成下图所示代码

 IntSupplier fib = new IntSupplier() {
 //初始化前两个数
            private int previous = 0;
            private int current = 1;

            public int getAsInt() {
            //拿到之前的数值
                int oldPrevious = this.previous;
                //计算下一个值,即当前的值和上一个值的和
                int nextValue = this.previous + this.current;
                //previous 向前一步只想current
                this.previous = this.current;
                //current向前一步指向nextValue
                this.current = nextValue;
                //返回最早的值
                return oldPrevious;
            }
        };
        IntStream.generate(fib).limit(10).forEach(System.out::println);

具体工作流程如下图所示,逻辑稍微绕一点,可以借此参考一下
java8实战——自定义流的构建_第3张图片

源码地址

https://github.com/shark-ctrl/Java8InAction

参考文献

Java 8 in Action

你可能感兴趣的:(#,Java8实战,java)