Java8新特性3——Stream

Java8新特性3——Stream

注:以下内容基于Java 8,所有代码都已在Java 8环境下测试通过

目录:

  • Java8新特性1——函数式接口&lambda表达式
  • Java8新特性2——方法引用
  • Java8新特性3——Stream

1. Stream

Stream(流) 是一个来自数据源的元素队列并支持聚合操作

  • 元素:特定类型的对象,形成一个队列
  • 数据源:流的来源。可以是集合、数组等
  • 聚合操作:类似SQL语句一样的操作

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定对集合进行的操作,可以执行非常复杂的查找、筛选、过滤、排序、聚合和映射数据等操作。通过配合 LambdaStream 方便的操作集合(Collection)。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

特点:

  • 不仅支持串行操作,还支持并行操作,简化了编写并行程序的过程

  • 不是数据结构,不会保存数据(类似工厂的流水线,只对产品进行加工而不会储存产品)

  • 惰性求值,流在中间操作过程中,只是对操作进行了记录,并不会立即执行,等到执行终端操作的时候才会进行实际的计算

使用流程:

  1. 创建流
  2. 操作流(分为中间操作和终端操作)

从支持数据处理操作的源生成元素序列.数据源可以是集合,数组或IO资源。

从操作角度来看,流与集合是不同的. 流不存储数据值; 流的目的是处理数据,它是关于算法与计算的。

如果把集合作为流的数据源,创建流时不会导致数据流动; 如果流的终止操作需要值时,流会从集合中获取值; 流只使用一次。

流中心思想是延迟计算,流直到需要时才计算值。

2. 创建Stream

主要有以下几种方式创建Stream。

2.1 通过集合创建

import java.util.ArrayList;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(-1);
        list.add(1);

        Stream<Integer> listStream = list.stream();//通过集合创建串行流
        Stream<Integer> parallelListStream = list.parallelStream();//通过集合创建并行流
    }
}

串行流在处理时,由一个线程按照顺序对其执行操作;而并行流在处理时,可以由多个线程对流并行操作,可以加快处理的速度。

2.2 通过数组创建

import java.util.Arrays;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;


public class Main {
    public static void main(String[] args) {
        int[] intArr = {0, 1, 2};
        double[] doubleArr = {1.1, 2.2, 3.3};

        IntStream intStream = Arrays.stream(intArr);//从 int 数组创建数值流
        DoubleStream doubleStream = Arrays.stream(doubleArr);//从 double 数组创建数值流
    }
}

该方法生成的是数值流IntStreamDoubleStream ),使用数值流可以避免计算过程中的拆箱装箱从而提高性能。

Stream API提供了 mapToIntmapToDoublemapToLong 三种方式将对象流(Stream)转换成对应的数值流,同时提供了 boxed 方法将数值流转换为对象流:

import java.util.ArrayList;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(-1);
        list.add(1);

        Stream<Integer> listStream = list.stream();//通过集合创建流

        IntStream intStream = listStream.mapToInt(Math::abs);//将流转换为数值流

        Stream<Integer> stream = intStream.boxed();//将数值流转换为流
    }
}

2.3 直接使用Stream的静态方法创建

主要有以下三种:

  • of() :将任意对象转换成 Stream
    • 参数:
      1. T... values :用于创建流的数据
  • iterator() :流迭代器,会一直迭代下去生成无限流,因此一般要结合 limit 方法指定流中元素的数量防止一直迭代
    • 参数:
      1. seed :初始元素
      2. f :函数,前一个元素使用该函数生成新的元素
  • generator() :流生成器,根据参数生成无限流,一般要结合 limit 方法指定流中元素的数量防止一直迭代
    • 参数:
      1. s:实现 Supplier函数式接口 ,为流提供值
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        Stream<Integer> stream1 = Stream.of(0, 1, 2);//将 int 转换成 Stream
        Stream<int[]> stream2 = Stream.of(new int[]{0, 1, 2});//将 int 数组转换成 Stream

        /**
         * 生成偶数
         * limit(8) 表示只生成 8 个
         */
        Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(8);


        /**
         * 生成字符串
         * limit(3) 表示只生成 3 个
         */
        Stream<String> stream4 = Stream.generate(() -> "Hello World").limit(3);
    }
}

2.4 通过文件创建

import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        try {
            Stream<String> fileStream = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

得到的 Stream 是文件中的每一行。

2.5 串行流与并行流相互转换

除了可以直接通过集合创建并行流,还可以通过 parallel() 把顺序流转换成并行流。

import java.util.ArrayList;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(-1);
        list.add(1);

        Stream<Integer> listStream = list.stream();//通过集合创建串行流
        Stream<Integer> parallelListStream = list.parallelStream();//通过集合直接创建并行流

        Stream<Integer> parallelListStream1 = listStream.parallel();//将串行流转换成并行流

        Stream<Integer> listStream1 = parallelListStream1.sequential();//将并行流转换成串行流
    }
}

3. 操作Stream

Stream的操作大致可以分为中间操作和终端操作两类。

  • 中间操作:对Stream中的数据进行转换、过滤、排序等操作,每次操作都返回一个新的Stream,可以有多个中间操作。可分为无状态操作和有状态操作
    • 无状态操作:元素的处理不受之前元素的影响
    • 有状态操作:该操作只有拿到所有元素之后才可以继续
  • 终端操作:对每个流只能进行一次终端操作,终端操作结束后流无法再次使用,终端操作会产生一个新的集合或值。可分为短路操作和非短路操作:
    • 短路操作:遇到某些符合条件的元素直接得到最终结果
    • 非短路操作:必须处理完所有元素才能得到最终结果
操作类型 常用API
无状态操作 unordered()、peek()、map()、mapToInt()、mapToLong()、mapToDouble()、filter()、filterMap()、filterMapToInt()、filterMapToLong()、filterMapToDouble()
有状态操作 limit()、distinct()、sorted()、skip()
短路操作 anyMatch()、allMatch()、findFirst()、findAny()、、
非短路操作 forEach()、forEachOrdered()、toArray()、reduce()、collect()、max()、min()、count()、forEachOrdered()

4. 代码实例

import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.iterate(0, (x) -> x + 1).limit(20);//从 0 开始生成 20 个整数
        stream = stream.filter(x -> x > 10)//过滤出大于10的数
                .map(x -> x - 20);//将过滤出的数减 20

        stream.forEachOrdered(System.out::println);//遍历输出
    }
}

你可能感兴趣的:(Java,SE学习笔记,java,Stream)