Stream流和函数式编程学习

144 阅读4分钟

stream流是为了方便操作集合和数组的,与Lambda表达式配合

流的操作可以分为两种类型

  1. 中间操作,可以有多个,每次返回一个新的流,可以进行链式操作,不可变性,不会修改原始数据源,也不会产生中间状态或副作用,每次返回一个新的流对象,保证数据的不可变性。
  2. 终端操作,只能有一个,每次执行完,流便关闭,这里要注意中间操作不会立即执行,只有到了终端操作,流才真正的开始遍历执行,也就是一次遍历执行多个操作,性能提升,也就是惰性要求。

首先创建流

1.数组:Arrays.stream()或者stream.of()创建流。查看of()方法源码发现其内部其实调用了Arrays.stream()方法,通过此方法将一组元素转换成Stream对象。

public static<T> Stream<T> of(T... values) {
    return Arrays.stream(values);
}

2.集合可以直接使用stream()方法创建流,因为该方法在Collection接口中。(集合还可以调用parallelStream()并行流,但我不会)

操作流

在此之前补充:java8新特性,新增函数式接口,下面是四个常见的接口

  1. Consumer:消费型接口,接受一个输入参数并且无返回值类型。

  2. Predicate:判断型接口,其中有一个test()的抽象方法,接受一个输入参数,返回一个布尔值结果。

     public interface Predicate<T> {
        boolean test(T t);```
    
    
    
  3. Supplier:供给型接口,无参数,返回一个结果。

  4. Function<T,R>:函数式接口,其中有一个抽象方法,第一个泛型接受一个输入参数,第二个泛型返回一个结果。

     public interface Function<T, R> {
    
    R apply(T t);
    

1.过滤filter():中间操作 接受一个Predicate函数作为参数,用于过滤Stream流中的元素,只有满足Predicate条件的元素会被保留下来

stream.filter(new Predicate<Student>() {
   @Override
   public boolean test(Student student) {
       return student.getScore()>70;\\这里可以简写下文再说
   }
}).forEach(System.out::println);

映射Map():中间操作 接受一个Function函数作为参数,用于对流中的元素进行映射转换,对每个元素应用apply方法后的返回值构成一个新的Stream流。

```
stream.map(new Function<Student, Integer>() {
@Override
public Integer apply(Student student) {
    return student.getScore()+10;//下文重写
}
   }).forEach(System.out::println);
```

flatMap

排序操作sorted:中间操作

对流中的数据进行排序,默认自然排序,或者传进一个比较器进行排序。自然排序通过排序对象实现Comparable 接口,实现compareTo方法,如果i=0, 也表明对象x与y排位上是相等的(并非意味x.equals(y) = true, 但是jdk api上强烈建议这样处理)如果返回数值>0,则调用对象大于参数对象(下一个对象),反之,调用者小于参数对象。=则相等。

```java

   @Override
   public int compareTo(Student o) {
    return this.score-o.score;
        }
```

第二种用法,传入一个比较器comparator,指定排序规则进行排序

  @Override
  public int compare(Student o1, Student o2) {
      return o1.getScore()-o2.getScore() !=0 ?o1.getScore()-o2.getScore():o1.getId()-o2.getId();
  }
}).forEach(System.out::println);

去重Distinct:中间操作

根据元素的equals() 和 hashCode() 方法来判断是否重复

截断操作limit和skip:中间操作

limit(n)保留流中的前n个元素,返回一个包含最多n个元素的新流,若流中的元素少于n个返回原始流

skip(n)跳过流的前n个元素,返回包含剩余元素的新流,若流中元素少于n返回空流。

组合流concat Stream中静态方法,返回一个新的流

终端操作

匹配操作 allMatch, anyMatch,noneMatch

统计个数count()统计流中元素个数返回long值

查找第一个findFirst()返回一个Optional对象

查造某一个findAny()返回一个Optional对象

lamdba表达式语法精简

  1. 参数类型可以省略,若省略,每个参数类型都需要省略

  2. 参数的小括号里面只有一个参数,小括号省略

  3. 如果方法体中只有一句代码,大括号省略

  4. 若方法体只有一句代码,且是return那么大括号省略且去掉return lambda表达式由三部分组成:参数列表,->,方法体。其本质是一个匿名内部类有一个抽象方法,重写这个方法。 lambda表达式对接口要求,接口定义的必须要实现的抽象方法只能是一个。 lambda的进阶方法引用 若存在一种情况,我们新建了多个接口的实现对象,其方法都是相同的,我们的接口方法需要修改,那么都需要更改,现在提供一种更高效的简写方法。方法引用的定义是:快速将一个lambda表达式的实现指向一个已经写好的方法。 语法说明:方法隶属者::方法名(静态方法隶属者类,非静态方法隶属者对象,隶属者不是接口,而是定义引用方法的类或者对象) 注意事项: 1. 被引用的方法参数数量以及类型要和接口中的