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

本章内容

1、筛选、切片

2、映射

3、查找、匹配

4、归约

5、数值流

6、构建流

7、小结

3. 查找和匹配

另一个常见的数据处理套路是看看数据集中的某些元素是否匹配一个给定的属性。Stream API通过allMatch、anyMatch、noneMatch、findFirst和findAny方法提供了这样的工具。

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

anyMatch方法可以回答“流中是否有一个元素能匹配给定的谓词”。

比如,你可以用它来看看菜单里面是否有素食可选择:

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

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

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

allMatch方法的工作原理和anyMatch类似,但它会看看流中的元素是否都能匹配给定的谓词,比如你可以用它来看看菜品是否有利于健康(即所有菜的热量都低于1000卡路里)

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

noneMatch

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

比如。你可以用noneMatch重写前面的例子:

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

anyMatch、allMatch和noneMatch这三个操作都用到了我们所谓的短路,这就是我们大家熟悉的Java中&&和||运算符短路在流中的版本。

3.3 查找元素

findAny方法将返回当前流中的任意元素。它可以与其他流操作结合使用。比如,你可能想找到一道素食菜肴。你可以结合使用filter和findAny方法来实现这个查询:

Optional<Dish> dish =
        menu.stream()
                .filter(Dish::isVegetarian)
                .findAny();

流水线将在后台进行优化使其只需走一遍,并在利用短路找到结果时立即结束。不过,Optional是个什么玩意???

Optional简介

Optional<T>类是一个容器类,代表一个值存在或不存在。在上面的代码中,findAny可能什么元素都没找到。Java8的库设计人员引入了Optional<T>,这样就不用返回众所周知容易出问题的null了。(我们在这个不会详细讨论optional,因为在后面的章节里会详细解释你的代码如何利用optional,避免与null检查相关的bug,不过现在了解下Optional里面几种可以迫使你显式地检查值是否存在或处理值不存在的情形的方法也不错。)

  • isPresent()将在Optional包含值的时候返回true,否则返回false;

  • isPresent(Consumer<T> block)会在值存在的时候执行给定的代码块。

  • T get()会在值存在的时候返回值,否则抛出一个NoSuchElement异常。

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

3.4 查找出第一个元素

findFirst方法,它的工作方式类似于findAny。

例如,给定一个数字列表,下面的代码能找出第一个平方能被3整除的数:

List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree =
        someNumbers.stream()
                .map(x -> x * x)
                .filter(x -> x % 3 == 0)
                .findFirst(); // 9

4. 归约

到目前为止,你见到过的终端操作都是返回一个boolean(allMatch类的)、void(forEach)或Optional对象(findAny等),你也见过使用collect来将流中的所有元素组合成一个List 。

本节,你将看到如何把一个流中的元素组合起来,使用reduce操作来表达更复杂的查询,比如“计算菜单中的总卡路里”或“菜单中卡路里最高的菜是哪一个。”此类查询需要将流中所有的元素反复结合起来,得到一个值,比如一个Integer。这样的查询可以被归类为归约操作(将流归约成一个值)。

4.1 元素求和

在我们研究如何使用reduce方法之前,先来看看如何使用for-each循环来对数字列表中的元素求和:

int sum = 0;
for (int x : numbers) {
    sum += x;
}

numbers中的每个元素都用加法运算符反复迭代得到结果。通过反复使用加法,你把一个数字列表归约成了一个数字。这段代码里有两个参数:

  • 总和变量的初始值,在这里是0

  • 将列表中所有元素结合在一起操作,在这里是+。

要是还能把所有的数字相乘,而不必去复制粘贴代码,岂不是很好?这正是reduce操作的用武之地,它对这种重复应用的模式做了抽象,你可以像下面这样对流中的所有元素求和:

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

reduce接受两个参数:

  • 一个初始值,这里是0;

  • 一个BinaryOperator<T>来将两个元素结合起来产生一个新值,这里就是我们用到的Lambda (a,b)->a+b;

无初始值

reduce还有一个重载的变体,它不接受初始值,但是会返回一个Optional对象:

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

这就是为什么结果被包裹在一个Optional对象里,以表明和可能不存在。

4.2 最大值和最小值

原来,只要用归约就可以计算最大值和最小值了!正如你前面看到的,reduce接受两个参数:

  • 一个初始值

  • 一个Lambda来把两个流元素结合起来并产生一个新值

Lambda是一步步用加法运算符应用到流中每个元素上的。

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值