Java Stream

一、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]
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小康2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值