目录

一、Stream流的概述

二、Stream流的获取

1.集合获取Stream流的方式

2.数组获取Stream流的方式

 三、Stream流的三种方法

1.获取Stream

2.中间方法

3.终结方法

四、Stream流的常用方法(中间方法)

五、Stream流的常见终结方法

六、收集Stream流

1.收集Stream的含义和作用

2.Stream流的收集方法


一、Stream流的概述

目的:用于简化集合和数组操作的API。

作用:简化集合、数组操作的API。结合了Lambda表达式。

Stream流式思想的核心:

1.先得到集合或者数组的Stream流(就是一根传送带)

2.把元素放上去

3.然后就用这个Stream流简化的API来方便的操作元素。

List<String> names = new ArrayList<>();
Collections.addAll(names,"张三丰","张无忌","赵敏","张三","王二娃","东方不败");
names.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(s -> System.out.println(s));
//.stream()是将集合names转换为stream流
//filter()是过滤器,是针对stream中的所有元素的筛选条件
//s -> 使用了Lambda表达式,含义为:创建一个对象(对象类型为stream中元素相同类型),箭头后是筛选条件
//同样的在foreach中使用了Lambda表达式,箭头后是操作

二、Stream流的获取

1.集合获取Stream流的方式

可以使用Collection接口中的默认方法stream​()生成流

集合名.stream();

//例如
Collection<String> names = new ArrayList<>();
Stream<String> nameStream = names.stream();

名称

说明

default Stream<E> stream​()

获取当前集合对象的Stream流

2.数组获取Stream流的方式

String[] names = new String[10];
Stream<String> nameStream1 = Arrays.stream(names);
Stream<String> nameStream2 = Stream.of(names);

名称

说明

public static <T> Stream<T> stream(T[] array)

获取当前数组的Stream流

public static<T> Stream<T> of(T... values) 

获取当前数组/可变数据的Stream流

 三、Stream流的三种方法

1.获取Stream

创建一条流水线,并把数据放到流水线上准备进行操作

(例如第一个代码块中的.stream())

2.中间方法

流水线上的操作。一次操作完毕之后,还可以继续进行其他操作。

(例如第一个代码块中的.filter())

3.终结方法

一个Stream流只能有一个终结方法,是流水线上的最后一个操作

(例如第一个代码块中的.foreach())

四、Stream流的常用方法(中间方法)

名称

说明

Stream<T> filter(Predicate<? super T> predicate)

用于对流中的数据进行过滤。

Stream<T> limit​(long maxSize)

获取前几个元素

Stream<T> skip​(long n)

跳过前几个元素

Stream<T> distinct​()

去除流中重复的元素。依赖(hashCode和equals方法)

Stream<T> map()加工流中元素,即对流中元素进行操作
Stream<T> sorted()将元素按自然顺序排列,自定义类需实现Comparable接口,或传Comparator接口参数

static <T> Stream<T> concat​(Stream a, Stream b)

合并a和b两个流为一个流

import java.util.*;

public class TestStream {
    public static void main(String[] args){
        List<String> names = new ArrayList<>();
        Collections.addAll(names,"张三丰","张无忌","赵敏","张三","王二娃","东方不败");
        names.stream().map(s-> new Student(s)).forEach(s -> System.out.println(s));
    }
}

class Student {
    String name;
    int age;

    public Student(String name) {
        this.name = name;
        this.age = name.length()*10;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


 输出结果:

中间方法也称为非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程

在Stream流中无法直接修改集合、数组中的数据。 

五、Stream流的常见终结方法

名称

说明

void forEach​(Consumer action)

对此流的每个元素执行遍历操作

long count​()

返回此流中的元素数

注意:终结操作方法,调用完成后流就无法继续使用了,原因是不会返回Stream了。

六、收集Stream流

1.收集Stream的含义和作用

收集Stream流的含义:就是把Stream流操作后的结果数据转回到集合或者数组中去。

Stream流:方便操作集合/数组的手段。

集合/数组:才是开发中的目的。

2.Stream流的收集方法

数组:Stream流的名称.toArray();

List<String> names = new ArrayList<>();
Collections.addAll(names,"张三丰","张无忌","赵敏","张三","王二娃","东方不败");
Stream<String> zhangStream = names.stream().filter(s -> s.startsWith("张"));
//默认转换为Object类型,所以只能用Object接收(可以后续进行其他处理)
Object[] zhangArray = zhangStream.toArray();
//String[] zhangArray = zhangStream.toArray(String[]::new);
//这样就能接收字符串数组了

名称

说明

R collect​(Collector collector)

开始收集Stream流,指定收集器

 Collectors工具类提供了具体的收集方式

名称

说明

public static <T> Collector toList​()

把元素收集到List集合中

public static <T> Collector toSet​()

把元素收集到Set集合中

public static  Collector toMap​(Function keyMapper , Function valueMapper)

把元素收集到Map集合中

public static void main(String[] args){
    List<String> names = new ArrayList<>();
    Collections.addAll(names,"张三丰","张无忌","赵敏","张三","王二娃","东方不败");
    Stream<String> zhangStream = names.stream().filter(s -> s.startsWith("张"));
    //流只能使用一次,使用完后就不能再次使用了
    List<String> zhangList = zhangStream.collect(Collectors.toList());
}

 流只能使用一次,使用完后就不能再次使用了

(使用完:遇到终结方法或是被转换为集合或数组)

七、Stream原理解析

上述讲了2个常见的获取流的类型以及他们获取流的方式

Collection的子类直接使用其继承自Collection的stream()方法即可获取流

数组类型使用Arrays.stream(T[] arr)获取对应的流

而Stream只是一个接口,其有几个实现类,对于一般对象使用的实现类为ReferencePipeline(也是个抽象类)的子类,而一般使用的则是其中的Head类

首先说Stream中的一些中间方法,类似map()、filter()等方法,在调用时都会创建一个StatelessOp对象;distinct()、sorted()、skip()、limit()等方法,在调用时会创建一个StatefulOp对象

StatelessOp和StatefulOp都是ReferencePipeline的子类,分别表示无状态操作和有状态操作,其中的状态指的是前面流元素的处理是否会直接影响后面流元素的处理

在创建对象时,会更新其中的上游和下游结点

previousStage.nextStage = this;

this.previousStage = previousStage;

 意思是将前置节点的下一个状态设置为当前新建节点;将当前节点的前置状态设为前置节点

最终所有方法执行完后就会变成一个双向链表,链表的头就是最初创建的Head

对于终结方法,会判断是否是并行(一般非并行)。非并行的情况下执行顺序如下:

1. AbstractPipeline.evaluate(TerminalOp<E_OUT, R> terminalOp)

  • TerminalOp有几个实现类:ForEachOp, ReduceOp, MatchOp, FindOp
  • 上述几个实现类中都有静态方法makeRef()返回TerminalOp对象,不同类有不同实现方式,其中重要的是重写的方法getOpFlags()
  • 在该方法中紧接着调用下面的方法->

2. TerminalOp.evaluateSequential(PipelineHelper<E_IN> helper, Spliterator<P_IN> spliterator)

  • 该方法是接口中的方法,具体实现同样在 ForEachOp, ReduceOp, MatchOp, FindOp 这几个类中
  • 该方法调用下面一个方法->

3. PipelineHelper.wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator)

  • 该方法为抽象方法,具体实现方法为AbstractPipeline.wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator
  • 其中会先调用AbstractPipeline.wrapSink(Sink<E_OUT> sink),其中会从链表尾开始逐个调用重写的opWrapSink(int flags, Sink<R> sink)
  • 然后将返回的wrappedSink作为参数传入AbstractPipeline.copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator)中
  • 在其中又调用spliterator.forEachRemaining(Consumer<? super E> action),这里也能显然看出来了,在该方法中调用action.accept()即可执行我们传入的方法了

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐