Java Stream

发布于:2023-10-25 ⋅ 阅读:(66) ⋅ 点赞:(0)

一、Stream 简介

1.1 Stream 的流式结构

Stream 是在 Java 8 随着函数式编程一起引入的新特性,简称为“流”,可以非常方便地处理 Java 中的集合,极大地加快代码的编写速度。

那为什么它被称为“流”呢?首先,Stream 本身翻译过来就有“流”的意思,此外,利用 Stream API 处理集合的方式非常像在一个工厂的生产流水线上处理产品的方式。集合内的元素就是产品,而 Stream 提供的一系列 API 就是处理这些产品的处理器:

elements of collection ----------------------->|-----> new collection (产生新的集合)
                         ↑          ↑          ↑
                       filter      map      collect
                        过滤      映射处理     收集

上述的 filter、sorted、map 和 collect 都是 Stream API,当然,上面的图只是举个形象的例子,并非说一定要这样用。当然,不仅仅是在逻辑处理上像“流”,其代码也非常像“流”:

import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;

public class Test {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("Java"); // 产品 1
        arrayList.add("ArrayList"); // 产品 2
        arrayList.add("Stream"); // 产品 3

        Predicate<String> filter = (String string) -> string.length() > 4; // 第 1 个“处理器”的处理规则
        Function<String, String> map = (String string) -> string.toLowerCase(); // 第 2 个“处理器”的处理规则

        List<String> newCollection = new ArrayList<>(); // 装新产品的容器

        newCollection = arrayList.stream().filter(filter).map(map).collect(Collectors.toList()); // 流式处理
        //                           ↑        ↑            ↑          ↑
        //                         产生流    过滤        映射处理     收集

        System.out.println(newCollection); // Output: [arraylist, stream]
    }
}

当然,我们要完成上述工作也不一定要用 Stream 来做,也可以用循环或者迭代器来做,下文详细讲述。

1.2 生成流

在 Java 8 中,集合有两个产生流的接口,stream 和 parallelStream,分别是一般流和并行流。这里不着重讲解并行流,只对一般的流进行讲解。

ArrayList<String> arrayList = new ArrayList<>(); // 创建一个数组列表(属于集合类的一种)
arrayList.stream(); // stream 方法返回此数组列表的流

stream 方法没有参数,返回值就是集合的流。

二、Stream 的方法

Stream 提供了一系列有用的方法,列举如下部分:

方法 描述 返回值
forEach 迭代每一个元素 void
map 对每一个元素做映射 Stream
filter 过滤掉一部分不符合要求的元素 Stream
limit 限制一部分元素 Stream
sorted 取前几个元素进行迭代 void
count 统计个数 void
skip 跳过前几个元素后迭代 Stream
collect 产生新的集合类 Stream

下面对每一个进行详细的讲述。

2.1 forEach 方法

forEach 可以视为 for 循环迭代集合类的语法糖,可以简化代码。比如我们要打印一个 ArrayList 中的每一个元素,可以分别用循环和 Stream API 的方式去写:

import java.util.ArrayList;

public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);

        for (Integer integer : arrayList) { // 采用 for 循环(迭代器)的方式
            System.out.println(integer);
        }

        arrayList.stream().forEach(System.out::println); // 采用 Stream 方式
    }
}

不过,集合类也有 forEach 这个方法,它和 Stream 的 forEach 功能近乎完全一样,但仍有以下爱区别:

  • Stream 的 forEach 是操作的流式数据,而集合类操作的是原始的集合元素;
  • Stream 的 forEach 方法是抽象方法,但集合类的不是抽象方法;
  • Stream 的 forEach 可以并行(效率可以更高),但集合类的只能串行;

下面就是集合类的 forEach 方法,在没有特别的要求下,其实集合类的 forEach 会更加常用一些。

arrayList.forEach(System.out::println);

这两个 forEach 都没有返回值(void)。

2.2 filter 方法

filter 翻译过来就是过滤的意思,因此 filter 方法可以对流中的数据进行过滤,只留下符合条件的数据,返回值是处理后的流:

arrayList.stream().filter(x -> x < 3).forEach(System.out::println); // 将 3 滤去了

学过 Python 朋友们对这个可能非常熟悉,对,它和 Python 的内置函数 filter 不能说是很像,只能说是一模一样啊!我们来看一下 Python 里面的 filter:

print(*filter(lambda x: x < 3, [1, 2, 3]))  # Output: 1 2
#        ↑          ↑              ↑
#       过滤    Lambda表达式      列表

每一种编程语言之间或多或少都是相同点的,善用类比思维,学习起来会事半功倍。

2.3 map 方法

讲完上面的 filter,看到这个 map 方法,学过 Python 的朋友可能又要笑了,这不就是 Python 内置的 map 函数嘛!对,他俩也如出一辙地相似。map 翻译过来有地图、表格的意思,体现的是一种映射关系,可以类比数学上函数的映射关系,也可以视为一种转换:

arrayList.stream().map(x -> x * x).forEach(System.out::println); // 映射为初始值的平方

和上面一样,我们来看看 Python 是怎么写的:

print(*map(lambda x: x**2, [1, 2, 3]))  # Output: 1 4 9

2.4 limit 方法

limit 翻译过来意为限制,有限的,因此它的作用也很好理解,就是只取流中前面的 n 个数据。那么很显然,它有一个参数,表示这个 n 的大小,返回限制后的流。n 可以大于集合类中的元素个数。

arrayList.stream().limit(2).forEach(System.out::println); // 只输出前 2 个

其实,Python 里面也有类似功能,就是切片。

2.5 sorted 方法

sort 翻译过来是排序的意思,加上 ed 后缀表示英语语法中的过去式,意为排序后的,因此不会对原来是数据产生影响,有返回值。sorted 方法有两个重载,一个是无参的,还有一个接收函数式接口,返回值都是排序后的 Stream。与此对应的是集合类本身就有的 sort 方法,是对其本身进行排序,没有返回值。(学 Python 的人又狂喜了)

没有参数的方法的功能很好猜,可以认为是默认的排序方式,而有参数的则是通过参数(比较器)的值自定义排序方式。

arrayList.sort(Comparator.naturalOrder()); // 按自然顺序对原始数据进行排序
arrayList.stream().sorted().forEach(System.out::println); // 从小到大排序(默认方式),不改变原始数据
arrayList.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println); // 倒序输出,不改变原始数据

这里也给出 Python 中类似的代码:

lst.sort()  # 对原始数据排序
lst.sort(key=lambda x: x ** 2)  # 按照平方和从小到大排序
print(sorted(lst))  # 不改变原始数据,返回新的列表
print(sorted(lst, key=lambda x: x ** 2))  # 按照平方和从小到大排序,返回新列表

2.6 count 方法

count 翻译过来为个数的意思,也就是说,它是用来统计流中有多少个数据的,没有参数,返回一个整数。

System.out.println(arrayList.stream().count()); // 输出数据个数

2.7 skip 方法

skip 意为跳过,作用和 limit 类似,只不过它是跳过前 n 个元素,与 limit 完全相反。n 可以大于集合类中的元素个数。

arrayList.stream().skip(2).forEach(System.out::println); // 不会输出前 2 个,其余正常

2.8 collect 方法

collect 意为收集,它可以将流数据重新收集起来并转换为某种集合类型。

import java.util.ArrayList;
import java.util.stream.Collectors;

public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        arrayList.add(4);

        ArrayList<Integer> newArrayList  = (ArrayList<Integer>) arrayList.stream().filter(x -> x < 4).map(x -> x * x).collect(Collectors.toList());

        System.out.println(newArrayList); // Output: [1, 4, 9]
    }
}
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到