Java8专题五(上)《使用流》

本章内容

1、筛选、切片

2、映射

3、查找、匹配

4、归约

5、数值流

6、构建流

7、小结

1. 筛选和切片

我们来看看如何选择流中的元素:用谓词筛选,筛选出各个不相同的元素忽略流中的头几个元素,或将流截短至指定长度

1.1 用谓词筛选

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

方法引用检查菜肴是否适合素食者。

List<Dish> vegetarianMenu = menu.stream()
        .filter(Dish::isVegetarian).collect(toList());

1.2 筛选各异的元素(去重)

流还支持一个叫做distinct的方法,他会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流。例如,以下代码会筛选出列表中所有的偶数,并确保没有重复。

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
        .filter(i -> i % 2 == 0)
        .distinct()
        .forEach(System.out::println);

//结果:
2
4

1.3 截短流

流支持limit(n)方法,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递给limit。如果流是有序的,则最多会返回前n个元素。

比如,你可以建立一个List,选出热量超过300卡路里的头三道菜:

List<Dish> dishes = menu.stream()
        .filter(d -> d.getCalories() > 300)
        .limit(3)
        .collect(toList());

以上小例子展示了filter和limit的组合。你可以看到,该方法只选出了符合谓词的头三个元素,然后就立即返回了结果。

请注意limit也可以用在无序流上,比如源是一个Set。这种情况下,limit的结果不会以任何顺序排列。

1.4 跳过元素

流还支持skip(n)方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。请注意,limit(n)和skip(n)是互补的!

例如下面的代码将跳过超过300卡路里的头两道菜,并返回剩下的。

List<Dish> dishes = menu.stream()
        .filter(d -> d.getCalories() > 300)
        .skip(2)
        .collect(toList());

在我们讨论映射操作之前,我们一起测验以上我们学过的内容吧。

测验1:筛选

你将如何利用流来筛选列表(1,2,3,4,5,6,7,8)中前2个偶数呢?


 

 

答案:你可以把filter和limit复合在一起来解决这个问题,并用collect将流转换成一个列表。

2. 映射

一个非常常见的数据处理套路就是从某个对象中选择信息。比如在SQL中你可以从表中选择一列。Stream API也通过map和flapMap方法提供了类似的工具。

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

流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上(使用映射一次,是因为他和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”)。

例如,下面的代码把方法引用Dish::getName传给了map方法,来提取流中菜肴的名称:

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

因为getName方法返回一个String,所以map方法输出的流的类型就是Stream<String>。

让我门看一个稍微不同的例子来巩固一下对map的理解。给定一个单词列表,你想要返回一个列表,显示每个单词的有几个字母,怎么做呢?

你需要对单词列表中的每一个元素应用一个函数,该函数接受一个单词,返回这个单词的长度。听起来好像可以正好可以用map方法去做!

List<String> words = Arrays.asList("Java 8", "Lambdas", "In", "Action");
List<Integer> wordLengths = words.stream()
        .map(String::length)
        .collect(toList());

现在回到提取菜名的例子。如果你要找出每道菜的名称有多长,怎么做?你可以像下面这样,再链接上一个map:

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

2.2 流的扁平化

你已经看到如何使用map方法返回列表中每个单词的长度了。让我们扩展一下:对于一张单词表,如何返回一张列表,列出里面各不相同的字符?例如给定单词列表[“Hello”,”World”],你想要返回列表["H","e","l", "o","W","r","d”]。

你可能会认为这很容易,你可以把每个单词映射成一张字符表,然后调用distinct来过滤重复的字符。

words.stream()
        .map(word -> word.split(""))
        .distinct()
        .collect(toList());

猜猜这个的结果会是什么?

List<String> words = Arrays.asList("hello", "hello");
List<String[]> t = words.stream()
        .map(word -> word.split(""))
        .distinct()
        .collect(Collectors.toList());
for(String[] s:t){
    for (String s1:s){
        System.out.print(s1);
    }
}

1.尝试使用map和Arrays.stream()

首先,你需要的是一个字符流,而不是一个数组流。有一个叫Arrays.stream()的方法可以接受一个数组并产生一个流。

例如:

String[] arrayOfWords = {"Goodbye", "World"};
Stream<String> streamOfwords = Arrays.stream(arrayOfWords);

问题还没有解决,这是因为,你先得到的是一个流的列表(更准备的说是Stream<String>)的确,你先是把每个单词转换成一个字母数组,然后把每个数组变成了一个独立的流。

这个和我们平时获取一个单词的字母组成有点不一样?如果只获取单词的字母组成,你可以这样写:

String str = "hello";
String[] s = str.split("");
List<String> strList = Arrays.stream(s).distinct().collect(Collectors.toList());

2. 使用flatMap

words.stream()
        .map(word -> word.split(""))
        .flatMap(word -> Arrays.stream(word))
        .distinct()
        .forEach(word -> System.out.print(word));

//结果:

helowrd

 

使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用map(Arrays::stream)时生成的单个流都被合并起来,即扁平化为一个流。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值