Java8实战-函数式数据处理-数值流与构建流

文章介绍了Java8StreamAPI中数值流的处理,包括原始类型特化流IntStream、DoubleStream和LongStream,避免装箱成本。通过mapToInt等方法映射到数值流,使用reduce或sum进行计算。此外,文章还讨论了如何通过iterate和generate创建无限流,并通过limit限制其大小。同时提到了Optional在处理可能缺失的值时的作用,以及如何从不同来源如数组、文件构建流。
摘要由CSDN通过智能技术生成

此文数值流、来自文件和数组等多种来源的流,最后是无限流


一.数值流

我们在可以使用reduce方法计算流中元素的总和。例如,你可以像下面这样计算菜单的热量:
int calories = menu.stream().map(Dish::getCalories) .reduce(0, Integer::sum);


这段代码的问题是,它有一个暗含的装箱成本。每个Integer都必须拆箱成一个原始类型,再进行求和。
Stream API还提供了原始类型流特化,专门支持处理数值流的方法。
map方法会生成一个Stream。虽然流中的元素是Integer类型,但Streams接口没有定义sum方法。

  1. 原始类型流特化

Java 8引入了三个原始类型特化流接口:IntStreamDoubleStreamLongStream,分别将流中的元素特化为int、long和double,从而避免了暗含的装箱成本。
每个接口都带来了进行常用数值归约的新方法,比如对数值流求和的sum,找到最大元素的max。
这些特化的原因并不在于流的复杂性,而是装箱造成的复杂性,即类似int和Integer之间的效率差异。

  • [1]映射到数值流

将流转换为特化版本的常用方法是mapToIntmapToDoublemapToLong。这些方法和map方法的工作方式一样,只是它们返回的是一个特化流,而不是Stream<T>。

例如,用mapToInt对menu中的卡路里求和:

int calories = menu.stream()//返回一个Stream<Dish>
					.mapToInt(Dish::getCalories)//返回一个IntStream
					.sum(); 

这里,mapToInt会从对象中获取calories属性,用一个Integer表示,并返回一个IntStream(而不是一个Stream)。然后调用IntStream接口中定义的sum方法求和了。
如果流是空的,sum默认返回0。
IntStream还支持其他的方便方法,如max、min、average等。

  • [2]转换回对象流

IntStream上的操作只能产生原始整数: IntStream 的 map 操作接受的 Lambda 必须接受 int 并返回 int (一个IntUnaryOperator)。
要把原始流转换成一般流可以使用boxed方法。

例如:

IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed(); 
  • [3]默认值OptionalInt

求和因为有一个默认值:0。如果你要计算IntStream中的最大元素0是错误的结果。
Optional可以用Integer、String等参考类型来参数化。对于三种原始流特化,也分别有一个Optional原始类型特化版本:OptionalIntOptionalDoubleOptionalLong

例如:

OptionalInt maxCalories = menu.stream()
				.mapToInt(Dish::getCalories)
 				.max(); 
//如果没有最大值的话,可以显式处理OptionalInt去定义一个默认值了:
int max = maxCalories.orElse(1);
  • [4]数值范围

Java 8引入了两个可以用于IntStream和LongStream的静态方法(rangerangeClosed),帮助生成数值范围,这两个方法都是第一个参数接受起始值,第二个参数接受结束值。range是不包含结束值的,而rangeClosed则包含结束值。

例如,生成1到100之间的所有数字,偶数出现个数:

IntStream evenNumbers = IntStream.rangeClosed(1, 100)//表示范围[1, 100]
 					.filter(n -> n % 2 == 0); //筛选偶数
System.out.println(evenNumbers.count());

结果:

50
如果改用IntStream.range(1, 100),则结果将会是49个偶数,因为range是不包含结束值的。

二.构建流

  1. 由值创建流

使用静态方法Stream.of,通过显式值创建一个流。
可以接受任意数量的参数。

例如,以下代码直接使用Stream.of创建了一个字符串流。然后,你可以将字符串转换为大写:

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

也可以使用empty得到一个空流:

Stream<String> emptyStream = Stream.empty();
  1. 由数组创建流

使用静态方法Arrays.stream从数组创建一个流。
它接受一个数组作为参数。

例如,将一个原始类型int的数组转换成一个IntStream:

int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
  1. 由文件生成流

java.nio.file.Files中的很多静态方法都会返回一个流。例如,一个很有用的方法是Files.lines,它会返回一个由指定文件中的各行构成的字符串流。

        long uniqueWords = 0;
        try (Stream<String> lines =
                     Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) {
            uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
                    .distinct()
                    .count();
        } catch (IOException e) {

        }
  1. 由函数生成流:创建无限流

Stream API提供了两个静态方法来从函数生成流:Stream.iterateStream.generate。这两个操作可以创建所谓的无限流:不像从固定集合创建的流那样有固定大小的流。
由iterate和generate产生的流会用给定的函数按需创建值,因此可以无穷无尽地计算下去。一般来说,
应该使用limit(n)来对这种流加以限制,以避免打印无穷多个值。

  • [ 1] 迭代
    iterate的简单例子:
Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println); 

iterate方法生成了一个所有正偶数的流:流的第一个元素是初始值0。然后加上2来生成新的值2,再加上2来得到新的值4,以此类推。
这种iterate操作基本上是顺序的,因为结果取决于前一次应用。此操作将生成一个无限流,这个流是无界、没有结尾,因为值是按需计算的,可以永远计算下去。

  • [ 2] 生成

与iterate方法类似,generate方法也可让你需生成一个无限流。但generate不是依次对每个新生成的值应用函数的。它接受一个Supplier类型的Lambda提供新的值。

Stream.generate(Math::random).limit(5).forEach(System.out::println); 

这段代码将生成一个流,其中有五个0到1之间的随机双精度数。
Math.Random静态方法被用作新值生成器。用limit方法显式限制流的大小,否则流将会无限长。

结果:

0.9410810294106129
0.6586270755634592
0.9592859117266873
0.13743396659487006
0.3942776037651241 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筑梦的熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值