美文网首页Java 杂谈程序员
《Java 8 in Action》Chapter 5:使用流

《Java 8 in Action》Chapter 5:使用流

作者: 后端小哥 | 来源:发表于2019-03-18 08:57 被阅读4次

流让你从外部迭代转向内部迭代,for循环显示迭代不用再写了,流内部管理对集合数据的迭代。这种处理数据的方式很有用,因为你让Stream API管理如何处理数据。这样Stream API就可以在背后进行多种优化。此外,使用内部迭代的话,Stream API可以决定并行运行你的代码。这要是用外部迭代的话就办不到了,因为你只能用单一线程挨个迭代。

1. 筛选和切片

1.1 用谓词筛选

该操作会接受一个谓词(一个返回 boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流。筛选出所有素菜

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

1.2 筛选各异的元素

返回一个元素各异(根据流所生成元素的 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);

1.3 截短流

流支持limit(n)方法,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递给limit。如果流是有序的,则最多会返回前n个元素。选出热量超过300卡路里的头三道菜

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

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

1.4 跳过元素

流还支持skip(n)方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。跳过超过300卡路里的头两道菜,并返回剩下的。

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

2. 映射

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

流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素。提取流中菜肴的名称:

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

2.2 流的扁平化

flatmap方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。单个流都被合并起来,即扁平化为一个流。例如,给定单词列表 ["Hello","World"],你想要返回列表["H","e","l", "o","W","r","d”]。

List<String> words = Arrays.asList("Java 8", "Lambdas", "In", "Action”);
List<String> uniqueCharacters = words.stream()
         .map(w -> w.split(""))
         .flatMap(Arrays::stream)
         .distinct()
         .collect(Collectors.toList());

3. 查找和匹配

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

anyMatch方法可以回答“流中是否有一个元素能匹配给定的谓词”。anyMatch方法返回一个boolean,因此是一个终端操作。比如,你可以用它来看看菜单里面是否有素食可选择:

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

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

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

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

和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<T>类(java.util.Optional)是一个容器类,代表一个值存在或不存在。Optional里面几种可以迫使你显式地检查值是否存在或处理值不存在的情形的方法也不错。

  • isPresent()将在Optional包含值的时候返回true, 否则返回false。
  • ifPresent(Consumer<T> block)会在值存在的时候执行给定的代码块。
  • T get()会在值存在时返回值,否则抛出一个NoSuchElement异常。
  • T orElse(T other)会在值存在时返回值,否则返回一个默认值。

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. 归约

归约操作 (将流归约成一个值)。用函数式编程语言的术语来说,这称为折叠(fold),因为你可以将这个操作看成把一张长长的纸(你的流)反复折叠成一个小方块,而这就是折叠操作的结果。

4.1 元素求和

reduce操作是如何作用于一个流的:Lambda反复结合每个元素,直到流被归约成一个值。
reduce接受两个参数:

  • 一个初始值,这里是0;
  • 一个BinaryOperator<T>来将两个元素结合起来产生一个新值,这里我们用的是 lambda (a, b) -> a + b。
int sum = numbers.stream().reduce(0, (a, b) -> a + b);

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

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

4.2 最大值和最小值

使用reduce来计算流中的最大值

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

要计算最小值,你需要把Integer.min传给reduce来替换Integer.max:

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

4.3 小结

公众号二维码.jpg

相关文章

网友评论

    本文标题:《Java 8 in Action》Chapter 5:使用流

    本文链接:https://www.haomeiwen.com/subject/hfvpmqtx.html