流(Stream)

目录

1.从外部迭代到内部迭代

2.内部迭代的实现机制

3.常用的流操作

3.1 Collect(toList())

3.2 map

3.3 filter

3.4 flatMap

3.5 max和min

3.6 reduce

4.高阶函数

5.总结


Java 8中新增的特性旨在帮助程序员写出更好的代码,其中对核心类库的改进是很关键的一部分。对核心类库的改进主要包括集合类的API和新引入的流(Stream),流使程序员得以站在更高的抽象层次上对集合进行操作。

1.从外部迭代到内部迭代

Java程序员在使用集合类时,一个通用的模式是在集合上进行迭代,然后返回处理的每一个元素。例如,我们在统计姓名为张三的学生数时,一般会写如下的代码:

      int count = 0;
      for (StudentDTO studentDTO : studentDTOList) {
          if (studentDTO.getName().equals("张三")) {
              count++;
          }
      }
      System.out.println(count);

这样的代码需要写很多的样板代码,而且将for循环改造成并行方式运行也很麻烦,需要修改每个for循环才能实现。 for循环和iterator循环本质上都是串行化的外部迭代。

与此相对应的是Stream实现的内部迭代:

int count2 = (int) studentDTOList.stream().filter(studentDTO -> studentDTO.getName().equals("张三")).count();

每一步的操作都对应于Stream接口的一个方法。为了找出姓名为张三的学生,首先需要对Stream对象进行过滤:filter。过滤在这里是指 “只保留通过某项检查的对象”。由于Stream API的函数式编程风格,我们并没有改变集合的内容,而是描述出Stream里的内容,count用于计算给定的Stream里包含多少个对象。

2.内部迭代的实现机制

像filter这样只描述Stream,最终不产生新集合的方法叫作惰性求值方法;而像count这样最终会从Stream产生值的方法叫作及早求值方法。 判断一个操作时惰性求值还是及早求值很简单:只需要看它的返回值。如果返回值是Stream,那么就是惰性求值;如果返回值是另一个值或者为 空,那么就是及早求值。使用这些操作的理想方式就是形成一个惰性求值链,最后用一个及早求值的操作返回想要的结果,这正是它的合理之处。惰性求值不会进行遍历操作,因此不会输出:

  studentDTOList.stream()
                .filter(studentDTO -> {
                    System.out.println(studentDTO.getName() + "1");
                    return studentDTO.getName().equals("张三");
                });

上述代码不会打印任何的信息,如果将同样的输出语句加入一个及早求值操作,就会输出

studentDTOList.stream()
                .filter(studentDTO -> {
                    System.out.println(studentDTO.getName() + "2");
                    return studentDTO.getName().equals("张三");
                }).count();

3.常用的流操作

3.1 Collect(toList())

Collect(toList())方法由Stream里的值生成一个列表,是一个及早求值操作。Stream的of方法使用一组初始值生成新的Stream。使用的实例如下:

 private static void collectTest() { 
     List<String> collected = Stream.of("a","b","c").collect(Collectors.toList()); 
     System.out.println(collected); 
} 

该实例展示了Stream操作的通用格式,首先由列表生成一个Stream,然后进行一些Stream上的操作,继而是collect操作,由Stream生成列表。

3.2 map

如果有一个函数可以将一种类型的值转换成另外一种类型,map操作就可以使用该函数,将一个流中的值转换成一个新的流。 注意:该map并不是Java中的Map数据结构,不是键值对,不要混淆了。

 private static void mapTest() { 
    List<String> collectedMap = Stream.of("a", "b", "c").map(string -> 
    string.toUpperCase()).collect(Collectors.toList());
    System.out.println(collectedMap); 
} 

3.3 filter

遍历数据并检查其中的元素时,可以尝试使用Stream中提供的新方法filter:

private static void filterTest() { 
        List<Integer> filterList = Stream.of(1, 7, 9, 4, 3, 5).filter(value -> value >= 5).collect(Collectors.toList()); 
} 

 filter操作可以看做if条件语句。

3.4 flatMap

flatMap方法可以用Stream替换值,然后将多个Stream连接成一个Stream。map操作可用一个新的值代替Stream中的值,但有时用户希望让map操作 有点变化,生成一个新的Stream对象取而代之。用户通常不希望结果是一连串的流,此时flatMap最能派上用场。

 private static void flatMapTest() {
        Integer a[] = {1, 2, 3};
        Integer b[] = {7, 8, 9};
        List<Integer> flatMap = Stream.of(a, b).flatMap(numbers -> Arrays.stream(numbers)).collect(Collectors.toList());
    }

3.5 max和min

Stream上常用的操作之一是求最大值和最小值:

  private static void maxAge() {
        StudentDTO maxId = studentDTOList.stream().max(Comparator.comparing(student -> student.getId())).get();
        System.out.println(maxId);
  }
java8提供了一个新的静态方法comparing,使用它可以方便的实现一个比较器。

max和min操作都属于更通用的一种编程模式。该模式的实现样板式代码如下:

 Object accumulator = initialValue;
 for (Object element:collection) {
     accumulator = combine(acctmulator,element);
 }

首先赋给accumulator一个初始值initialValue,然后在循环体中,通过调用combine函数,拿accumulator和集合中的每一个元素做运算, 再将运算结果赋给accumulator,最后accumulator的值就是想要的结果。

3.6 reduce

reduce操作可以实现从一组值中生成一个值,在上面中提到的count,min和max方法,因为常用而被纳入标准库中。事实上,这些方法都是reduce操作。下图展示了如何通过reduce操作对Stream中的数字之和,以0作为起点——一个空Stream的求和结果,每一步都将Stream中的元素累加到accumulator,遍历至Stream中的最后一个元素时,accumulator的值就是所有元素的和。

Lambda表达式就是reducer,它执行求和操作,有两个参数:传入Stream中的当前元素和acc。将两个参数相加,acc是累加器,保存着当前累加结果。 Lambda表达式的返回值是最新的acc,是上一轮acc的值和当前元素相加的结果。另一个参数0表示累加结果acc的初始值。

实现的代码如下:

 private static void reduceTest() {
        int sum = Stream.of(1, 2, 3, 4, 5).reduce(0, (acc, element) -> acc + element);
 }

4.高阶函数

高阶函数是指接受另外一个函数作为参数,或返回一个函数的函数。因此如果函数的参数列表中包含函数接口,或该函数返回一个函数接口,那么该函数就是高阶函数。map操作是一个高阶函数,因为它的mapper参数是一个函数,事实上上面介绍的Stream接口中,几乎所有的函数都是高阶函数。

5.总结

  • 内部迭代将更多控制权交给了集合类。
  • 和 Iterator 类似, Stream 是一种内部迭代方式。
  • 将 Lambda 表达式和 Stream 上的方法结合起来, 可以完成很多常见的集合操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值