Java 8实战-第五章

183 阅读5分钟

5 使用流

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

5.1 筛选和切片

如何选择流中的元素

5.1.1 用谓词筛选

Streams接口支持filter方法,该操作会接受一个谓词作为参数,并返回一个包括所有复合谓词的元素的流。

5.1.2 筛选各异的元素

流支持distinct的方法,他会返回一个元素各异的流。

5.1.3 截短流

流支持limit(n)方法,该方法会返回一个不超过给定长度的流。

5.1.4 跳过元素

流还支持skip(n)方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。

5.2 映射

Stream通过map和flatMap方法提供了映射工具

5.2.1 对流中每一个元素应用函数

List<Integer> dishNameLengths = menu.stream().map(Dish::getName).map(String::length).collect(toList());

流支持map方法,接受一个函数作为参数,这个函数会被应用到每个元素上,并将其映射成一个新的元素。

5.2.2 流的扁平化

List<String> uniqueCharacters = words.stream().map(w -> w.split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());

Arrays.stream()的方法可以接受一个数组并产生一个流,使用flatMa配方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。

5.3 查找和匹配

Stream API通过allMatch、anyMatch、noneMatch、findFirst和findAny方法提供了这样的工具。

5.3.1 检查谓词是否至少匹配一个元素

if (menu.stream().anyMatch(Dish::isVegetarian)){
System.out.println("The menu is (somewhat) vegetarian friendly!!");
}

anyMatch方法返回一个boolean,因此是一个终端操作。

5.3.2 检查谓词是否匹配所有元素

allMatch方法的工作原理和anyMatch类似,确定流中的元素是否都能匹配给定的谓词。

boolean isHealthy = menu.stream().allMatch(d -> d.getCalories() <1000);

和allMatch相对的是noneMatch,确定流中没有任何元素与给定的谓词匹配。

boolean isHealthy = menu.stream().noneMatch(d -> d.getCalories() >= 1000);

5.3.3 查找元素

findAny方法将返回当前流中的任意元素。Optional是一个容器类,代表一个值存在或不存在。

menu.stream().filter(Dish::isVegetarian).findAny().ifPresent(d -> System.out.println(d.getName());

5.3.4 查找第一个元素

想找到流中满足条件的第一个元素,为此有一个FindFirst方法。

5.4 规约

将一个流中的元素组合起来,使用reduce操作来表达更复杂的查询,这样的查询归类为规约操作。

5.4.1 元素求和

int sum = numbers.stream().reduce(0, (a, b) -> a + b);
int product = numbers.stream().reduce(1, (a, b) -> a * b);

无初始值

Optional<Integer> sum = numbers.stream().reduce((a,b) -> a + b);

5.4.2 最大值和最小值

Optional<Integer> max = numbers.stream.reduce(Integer::max);
Optional<Integer> min = numbers.stream.reduce(Integer::min);

需要区分流的操作是有状态还是无状态的,才能去定夺是否使用并行的操作,将stream改成parallelStream。

5.5 付诸实践

找出2011年发生的所有交易,并按交易额排序

List<Transaction> tr2011 = transactions.stream().filter(transaction -> 2011 == transaction.getYear()).sort(comparing(Transaction::getValue)).collect(toList());

交易员都在哪些不同的城市工作过

List<String> cities = transactions.stream().map(Transaction::getTrader).map(Trader::getCity).distinct().collect(toList());

找到交易额最小的交易

Optional<Transaction> smallestTransaction = transaction.stream().min(comparing(Transaction::getValue));

5.6 数值流

Stream API还提供了原始类型流特化,专门支持处理数值流的方法。

5.6.1 原始类型流特化

Java 8引入了三个原始类型特化流接口来解决拆箱的消耗,IntStream、DoubleStream和LongSteam。

int calories = menu.stream().mapToInt(Dish::getCalories).sum();

有一个Optional原始类型特化版本:OptionalInt、OptionalDouble、OptionalLong。

Optional maxCalories = menu.stream().mapToInt(Dish::getCalories).max();

5.6.2 数值范围

Java 8引入了IntStream和LongStream的静态方法,帮助生成这种范围:range和rangeClosed。 IntStream evenNumbers = IntStream.rangeClosed(1,100).filter(n -> n % 2 == 0);

5.6.3 数值流应用:勾股数

Stream<int[]> pythagoreanTriples = IntStream.rangeClosed(1,100).boxed()
.flatMap(a -> IntStream.rangClosed(a,100)
.filter(b -> Math.sqrt(a*a + b*b) % 1 ==0)
.mapToObj(b -> new int[] {a,b,(int)Math.sqrt(a*a + b*b)}));

pythagoreanTriples.limit(5).collect(toList());

5.7 构建流

从值序列、数组、文件来创建流,甚至由生成函数来创建无限流!

5.7.1 由值创建流

Stream<String> stream = Stream.of("Java 8"," Lambdas","Action");

5.7.2 由数组创建流

int numbers = {2,3,5,7,11,13};
int sum = Arrays.stream(numbers).sum;

5.7.3 由文件生成流

使用Files.lines得到一个流,其中的每个元素都是给定文件的一行,可以对line调用split方法将行拆分成单词。

5.7.4 由函数生成流:创建无限流

Stream API提供了两个静态方法来从函数生成流:Stream.iterate和Stream.generate。

5.8 小结

这一章很长,但是很有收获!现在你可以更高效地处理集合了。事实上,流让你可以简洁地表达复杂的数据处理查询。此外,流可以透明地并行化。以下是你应从本章中学到的关键概念。

Streams API可以表达复杂的数据处理查询。常用的流操作总结在表5-1中。

你可以使用filter、distinct、skip和limit对流做筛选和切片。

你可以使用map和flatMap提取或转换流中的元素。

你可以使用findFirst和 findAny方法查找流中的元素。你可以用allMatch、noneMatch和anyMatch方法让流匹配给定的谓词。

这些方法都利用了短路:找到结果就立即停止计算;没有必要处理整个流。

你可以利用reduce方法将流中所有的元素迭代合并成一个结果,例如求和或查找最大元素。

filter和map等操作是无状态的,它们并不存储任何状态。reduce等操作要存储状态才能计算出一个值。sorted和distinct等操作也要存储状态,因为它们需要把流中的所

有元素缓存起来才能返回一个新的流。这种操作称为有状态操作。

流有三种基本的原始类型特化:IntStream、DoubleStream和LongStream。它们的操作也有相应的特化。

流不仅可以从集合创建,也可从值、数组、文件以及iterate与generate等特定方法创建。

无限流是没有固定大小的流