流
流简介
流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。
简短的定义就是“从支持数据处理操作的源生成的元素序列”。
流只能遍历一次。遍历完之后,我们就说这个流已经被消费掉了。你可以从原始数据源那里再获得一个新的流来重新遍历一遍,就像迭代器一样(这里假设它是集合之类的可重复的源,如果是I/O通道就没戏了)。
流操作
流操作分类
对流的操作可以分为两类:中间操作、终端操作。
如下方代码所示,filter, map, limit
方法的返回值都是流,可以连成一条流水线,它们就是中间操作。collect
触发流水线执行并关闭,它就是终端操作。
可以连接起来的流操作称为中间操作,关闭流的操作称为终端操作。
List<String> names = menu.stream()
.filter(d -> d.getCalories() > 300) // 中间操作
.map(Dish::getName) // 中间操作
.limit(3) // 中间操作
.collect(toList()); // 终端操作
使用流
流的使用一般包括三件事:
- 一个数据源(如集合)来执行一个查询;
- 一个中间操作链,形成一条流的流水线;
- 一个终端操作,执行流水线,并能生成结果。
常见的中间操作有:filter,map, limit, sorted, distinct。
常见的终端操作有:forEach, count, collect。
筛选和切片
- 用谓词切片。
如下方的filter()
方法。接收一个返回boolean值的函数作为参数,并返回一个包含了符合该条件的元素的流。
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());
- 筛选各异的元素(去重)
如下方的distinct()
方法,会在filter方法返回的偶数流中,去掉重复的元素。
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
- 截短流
流支持limit(n)
方法,该方法会返回一个不超过给定长度n
的流。如下图,第3行的limit(3)
方法会返回流中的前3个元素。
如果limit方法的参数超过了流中元素的数量,limit返回的流会包含所有元素。不会报错。
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList());
- 跳过元素
流还支持skip(n)
方法,返回一个扔掉了前n
个元素的流。如果流中元素不足n个,则返回一个空流。
List<Integer> list = Arrays.asList(1, 2, 3, 1, 2, 4, 1).stream()
.skip(10) // 跳过10个元素,会返回一个空流。
.collect(Collectors.toList());
映射
Stream API通过
map
和flatMap
方法提供了映射方法,用来提取某些对象中的信息。
- 对流中每个元素应用函数
如下方代码,把方法引用Dish::getName
传给了map
方法,来提取流中菜肴的名称。因为getName方法返回一个String,所以map方法输出的流的类型就是Stream<String>
。
List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(toList());
也可以像下方代码这样,对流中每个字符串元素,返回它们的长度。最终返回Stream<Integer>
。
List<String> words = Arrays.asList("Java 8", "Lambdas", "In", "Action");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList())
- 流的扁平化
如下方代码所示,map()
方法会将原始流内的每个字符串转换为字符串数组,该方法返回一个数组串数组流Stream<String[]>
。而flatMap()
方法,会将流扁平化,对字符串数组流中的每个数组,都应用Arrays::stream
方法,将每个数组都转成字符串流Stream<String>
的内容,并返回这个字符串流。
Stream<String> stream = Arrays.asList("Hello", "World").stream()
.map(s -> s.split(""))
.flatMap(Arrays::stream);
使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用
Arrays::stream
时生成的单个流都被合并起来,即扁平化为一个流。一言以蔽之,flatmap方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。
再看一个稍微复杂的例子:给定两个数字列表,如何返回所有的数对呢?如,给定 int[] arr1 = {1, 2, 3},int[] arr2 = {3, 4};需要返回_[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)_
List<int[]> pairs = Arrays.asList(1, 2, 3).stream()
.flatMap(i -> Arrays.asList(3, 4).stream().map(j -> new int[]{i, j}))
.collect(Collectors.toList());
再进一步,如果只返回总和能被3整除的数对呢?加一个筛选即可。如下方代码所示:
List<int[]> pairs = Arrays.asList(1, 2, 3).stream()
.flatMap(
i -> Arrays.asList(3, 4).stream().
filter(j -> (i+j)%3 == 0)
.map(j -> new int[]{i, j})
).collect(Collectors.toList());
查找和匹配
主要有这些方法:allMatch、anyMatch、noneMatch、findFirst和findAny
。
规约
数值流
构建流
- 0很棒
- 0不错
- 0一般
- 0???