Stream API:Java 8 中的数据流式处理指南

Hello , 大家好 , 本篇文章给大家讲解的是 Java 8 中的 Stream 流 .
在 Java 8 中 , 引入了 Streams API,它允许以声明式方式处理数据集合。Stream API 提供了一种高级迭代器,可以用来执行序列化操作,如筛选、转换、聚合等。它支持并行操作,可以利用多核处理器来加速数据处理。
在日常开发过程中 , 使用 stream 流能够显著提升开发效率 , 极大提升生产力 , 非常推荐使用 .
在这里插入图片描述

一 . 体验 Stream 流

需求 : 按照下面的要求完成集合的创建和遍历

  1. 创建一个集合 , 存储多个字符串元素
  2. 把集合中所有以 “张” 开头的元素存储到一个新的集合
  3. 把 “张” 开头的集合中的长度为 3 的元素存储到另外一个新的集合中
  4. 遍历上一步得到的集合

那我们使用原始的方式 , 代码就是这个样子

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张四丰");
        list.add("张五");
        list.add("李四");
        list.add("王二麻子");

        List<String> result = new ArrayList<>();
        // 1. 遍历 list 集合
        for (String s : list) {
            // 2. 判断元素是否以 "张" 开头
            if (s.startsWith("张")) {
                // 3. 判断元素长度是否是 3
                if (s.length() == 3) {
                    // 4. 将元素添加到 result 集合中
                    result.add(s);
                }
            }
        }
        System.out.println(result);
    }
}

如果我们使用的是 Stream 流的形式 , 那代码就会被简化成这个样子

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张四丰");
        list.add("张五");
        list.add("李四");
        list.add("王二麻子");

        // Stream 流的写法
        List<String> result = new ArrayList<>();
        list.stream()
                .filter(name -> name.startsWith("张"))
                .filter(name -> name.length() == 3)
                .forEach(name -> System.out.println(name));
    }
}

那我们通过这个案例就可以看出 Stream 流的作用就是针对集合进行功能简化开发 , 并且 Stream 流的使用通常需要搭配 Lambda 表达式来使用 .

那 Stream 流我们就可以理解成流水线的过程 , 一步一步地筛选掉不满足条件的数据 .

那我们也可以得到 Stream 流的三类方法

  1. 获取 Stream 流 : 创建一条流水线 , 并把数据放到流水线上准备进行操作
  2. 中间方法 : 流水线上的操作 , 一次操作结束之后还可以继续进行其他操作
  3. 终结方法 : 一个 Stream 流只能有一个终结方法 , 是流水线上最后一道操作

二 . 获取 Stream 流对象

获取 Stream 流就是创建一条流水线 , 并把数据放到流水线上准备进行操作 .

2.1 单列集合

单列集合就是我们之前学习过的 Collection (List、Set …)

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

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

public class Demo2 {
    public static void main(String[] args) {
        method01();
    }

    private static void method01() {
        // 单列集合
        ArrayList<String> list = new ArrayList<>();
        list.add("甄嬛");
        list.add("沈眉庄");
        list.add("安陵容");

        // 获取 Stream 对象
        // 直接调用 stream 方法即可
        Stream<String> stream = list.stream();
        // 打印 stream 流中的内容
        stream.forEach(s -> System.out.println(s));
    }
}

而我们一般情况下不会将 stream 流拆分开来写

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

public class Demo2 {
    public static void main(String[] args) {
        method01();
    }

    private static void method01() {
        // 单列集合
        ArrayList<String> list = new ArrayList<>();
        list.add("甄嬛");
        list.add("沈眉庄");
        list.add("安陵容");

        // 获取 Stream 对象
        // 直接调用 stream 方法即可
        list.stream().forEach(s -> System.out.println(s));
    }
}

我们看一下运行结果

2.2 哈希表集合

哈希表集合需要间接的生成流 , 可以先通过 keySet 或者 entrySet 获取一个 Set 集合 , 再获取 Stream 流

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

public class Demo2 {
    public static void main(String[] args) {
        method02();
    }

    private static void method02() {
        // 双列集合
        HashMap<String, Integer> map = new HashMap<>();
        map.put("甄嬛", 18);
        map.put("沈眉庄", 19);
        map.put("安陵容", 20);

        // 获取 Stream 流
        // 双列集合不能直接获取 Stream 流
        // 先获取到所有的键, 再把这个 Set 集合中所有的键放到 Stream 流中
        map.keySet().stream().forEach(s -> System.out.println(s));// 打印出所有的键

        // 先获取到所有的键值对对象, 再把这个 Set 集合中所有的键值对对象都放到 Stream 流中
        map.entrySet().stream().forEach(s -> System.out.println(s));// 打印出所有的键值对对象
    }
}

运行一下 , 看一下效果

2.3 数组

数组可以通过 Arrays 中的静态方法 stream 生成流

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

public class Demo2 {
    public static void main(String[] args) {
        method03();
    }

    private static void method03() {
        // 数组
        int[] arr = {1, 2, 3, 4, 5};

        // 获取 Stream 流
        Arrays.stream(arr).forEach(s -> System.out.println(s));
    }
}

2.4 同种数据类型的多个元素

比如 :

  • 1 , 2 , 3 , 4 , 5 …
  • “aaa” , “bbb” , “ccc” , “ddd” …

我们需要使用 Stream.of(T… values) 生成流

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

public class Demo2 {
    public static void main(String[] args) {
        method04();
    }

    private static void method04() {
        // 同种数据类型的多个数据
        // 将同一类型的数据放到 of 中即可
        Stream.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(s -> System.out.println(s));
    }
}

2.5 小结

单列集合 : 集合对象.stream()

双列集合 : 不能直接获取 , 需要间接获取

  • 获取键的集合 : 集合对象.keySet().stream();
  • 获取键值对的集合 : 集合对象.entrySet().stream();

数组 : Arrays.stream(数组名称)

同种数据类型的多个数据 : Stream.of(多个数据);

三 . 中间方法

中间方法指的就是流水线上的操作 . 一次操作完毕之后 , 还可以继续进行其他的操作 .

3.1 filter 方法

filter 方法用于对流中的数据进行过滤 . 对给定的参数进行判断 , 返回一个布尔值

3.1.1 匿名内部类

这种方式需要我们传入一个 Predicate 匿名内部类对象

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Demo2 {
    public static void main(String[] args) {
        method05();
    }

    // 需求 : 筛选出姓张的用户
    private static void method05() {
        List<String> list = new ArrayList<>();
        list.add("张凯丽");
        list.add("张韶涵");
        list.add("张学友");
        list.add("李四");

        // 获取 Stream 流
        // 使用 filter 可以设置筛选条件
        // 这个方法需要我们传入一个 Predicate 匿名内部类对象
        list.stream().filter(
                // filter 方法获取流中的每一个数据
                // 而 test 方法中的 s, 就一次表示流中的每一个数据
                // 我们只需要在 test 方法中对 s 进行判断即可
                new Predicate<String>() {
                    @Override
                    public boolean test(String s) {
                        boolean result = s.startsWith("张");
                        return result;
                    }
                }
        ).forEach(s -> System.out.println(s));
    }
}

3.1.2 lambda 表达式

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Demo2 {
    public static void main(String[] args) {
        method06();
    }

    // 需求 : 筛选出姓张的用户
    private static void method06() {
        List<String> list = new ArrayList<>();
        list.add("张凯丽");
        list.add("张韶涵");
        list.add("张学友");
        list.add("李四");

        // 获取 Stream 流
        // 使用 filter 可以设置筛选条件
        list.stream().filter(
                (String s) -> {
                    boolean result = s.startsWith("张");
                    return result;
                }
        ).forEach(s -> System.out.println(s));
    }

}

那我们还可以对这个 lambda 表达式进一步简化

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Demo2 {
    public static void main(String[] args) {
        method06();
    }

    // 需求 : 筛选出姓张的用户
    private static void method06() {
        List<String> list = new ArrayList<>();
        list.add("张凯丽");
        list.add("张韶涵");
        list.add("张学友");
        list.add("李四");

        // 获取 Stream 流
        // 使用 filter 可以设置筛选条件
        list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));
    }
}

3.2 limit 方法

limit 方法是用来截取指定参数个数的数据

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Demo2 {
    public static void main(String[] args) {
        method07();
    }

    private static void method07() {
        List<String> list = new ArrayList<>();
        list.add("张凯丽");
        list.add("张韶涵");
        list.add("张学友");
        list.add("张靓颖");
        list.add("张碧晨");
        list.add("张信哲");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        // 截取指定参数个数的数据
        list.stream().limit(2).forEach(s -> System.out.println(s));
    }
}

3.3 skip 方法

skip 方法是跳过指定参数个数的数据

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Demo2 {
    public static void main(String[] args) {
        method07();
    }

    private static void method07() {
        List<String> list = new ArrayList<>();
        list.add("张凯丽");
        list.add("张韶涵");
        list.add("张学友");
        list.add("张靓颖");
        list.add("张碧晨");
        list.add("张信哲");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        // 跳过指定参数个数的数据
        // 去除前面的, 保留后面的
        list.stream().skip(2).forEach(s -> System.out.println(s));
    }
}

3.4 concat 方法

concat 方法是合并 a 和 b 两个流为一个流

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Demo2 {
    public static void main(String[] args) {
        method07();
    }

    private static void method07() {
        List<String> list = new ArrayList<>();
        list.add("张凯丽");
        list.add("张韶涵");
        list.add("张学友");
        list.add("张靓颖");
        list.add("张碧晨");
        list.add("张信哲");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        List<String> list2 = new ArrayList<>();
        list2.add("张三丰");
        list2.add("张无忌");
        
        // 合并两个流为一个流
        Stream<String> stream1 = list.stream();
        Stream<String> stream2 = list2.stream();
        Stream<String> concat = Stream.concat(stream1, stream2);
        concat.forEach(s -> System.out.println(s));

    }
}

那我们还可以进行简写

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Demo2 {
    public static void main(String[] args) {
        method07();
    }

    private static void method07() {
        List<String> list = new ArrayList<>();
        list.add("张凯丽");
        list.add("张韶涵");
        list.add("张学友");
        list.add("张靓颖");
        list.add("张碧晨");
        list.add("张信哲");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        // 截取指定参数个数的数据
        // 保留前面的, 去除后面的
        // list.stream().limit(2).forEach(s -> System.out.println(s));

        // 跳过指定参数个数的数据
        // 去除前面的, 保留后面的
        // list.stream().skip(2).forEach(s -> System.out.println(s));

        List<String> list2 = new ArrayList<>();
        list2.add("张三丰");
        list2.add("张无忌");
        // 合并两个流为一个流
        Stream.concat(list.stream(), list2.stream()).forEach(s -> System.out.println(s));
    }

3.5 distinct 方法

distinct 方法用来去除流中重复的元素 , 底层依赖 hashCode 方法和 equals 方法

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Demo2 {
    public static void main(String[] args) {
        method08();
    }

    private static void method08() {
        List<String> list = new ArrayList<>();
        list.add("张凯丽");
        list.add("张韶涵");
        list.add("张学友");
        list.add("张靓颖");
        // 设置一些重复数据
        list.add("张三");
        list.add("张三");
        list.add("张三");
        list.add("张三");

        // 去除流中重复的元素
        // 底层依赖 hashCode 和 equals 实现的
        list.stream().distinct().forEach(s -> System.out.println(s));
    }
}

四 . 终结方法

一个 Stream 流只能有一个终结方法

常见的终结操作方法如下 :

  • forEach : 对流中的每个元素进行操作
  • count : 返回流中的元素数量

4.1 forEach 方法

forEach 最基础的方法的形参是一个 Consumer 匿名内部类

我们还可以使用 lambda 表达式来进行缩写

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Demo2 {
    public static void main(String[] args) {
        method09();
    }

    private static void method09() {
        List<String> list = new ArrayList<>();
        list.add("张凯丽");
        list.add("张韶涵");
        list.add("张学友");
        list.add("张靓颖");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        list.stream().forEach(new Consumer<String>() {
            // 在 forEach 方法的底层, 会循环获取到流中的每个数据, 并循环调用 accept 方法, 将每一个数据传递给 accept 方法
            // s 就依次表示了流中的每一个数据
            @Override
            public void accept(String s) {
                // 对数据的处理可以直接写到 accept 方法中
                System.out.println(s);
            }
        });

        // lambda 表达式简写
        list.stream().forEach(
                (String s) -> {
                    System.out.println(s);
                }
        );

        // lambda 表达式更进一步简写
        list.stream().forEach(s -> System.out.println(s));
    }
}

4.2 count 方法

count 方法用于返回流中的元素个数

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Demo2 {
    public static void main(String[] args) {
        method10();
    }

    private static void method10() {
        List<String> list = new ArrayList<>();
        list.add("张凯丽");
        list.add("张韶涵");
        list.add("张学友");
        list.add("张靓颖");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        // 返回流中的元素个数
        long count = list.stream().count();
        System.out.println(count);
    }
}

五 . 收集操作

需求 : 过滤元素并遍历集合

我们定义一个集合 , 并且添加一些整数 1、2、3、4、5、6、7、8、9、10 .

将集合中的奇数删除 , 只保留偶数 .

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Demo2 {
    public static void main(String[] args) {
        method11();
    }

    // 我们定义一个集合, 并且添加一些整数 1、2、3、4、5、6、7、8、9、10 .
    // 将集合中的奇数删除, 只保留偶数 .
    private static void method11() {
        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

        // 基本写法
        list.stream().filter(
                // 参数为 Integer 类型
                (Integer i) -> {
                    // 在这个位置设置条件
                    return i % 2 == 0;
                }
        );

        // 简化写法
        list.stream().filter(num -> num % 2 == 0).forEach(num -> System.out.println(num));
    }
}

但是我们此时打印一下原来的集合

结论 : 在 Stream 流中无法直接修改集合、数组等数据源中的数据 .

但是如果我们使用 Stream 流的方式操作完毕之后 , 那我还想把流中的数据保存起来 , 该怎么办呢 ?

我们需要使用 Collectors 这个工具类

  • toList() : 把元素收集到 List 集合中
  • toSet() : 把元素收集到 Set 集合中
  • toMap() : 把元素收集到 Map 集合中
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Demo2 {
    public static void main(String[] args) {
        method12();
    }

    private static void method12() {
        // 10 这个数据是重复的
        List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10));

        // filter 方法负责过滤数据
        // collect 只负责获取流中剩余的数据, 但是他并不负责创建容器, 也不负责把数据添加到容器中
        // Collectors.toList() 在底层会创建一个 List 集合, 并且把所有的数据添加到 List 集合中
        List<Integer> list2 = list1.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());
        System.out.println(list2);

        // Collectors.toSet() 在底层会创建一个 Set 集合, 并且把所有的数据添加到 Set 集合中
        Set<Integer> list3 = list1.stream().filter(num -> num % 2 == 0).collect(Collectors.toSet());
        System.out.println(list3);
    }
}

我们看一下运行结果


对于 Java8 中的 Stream 流的介绍就到这里 , 如果对你有帮助的话 , 还请一键三连支持~
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

加勒比海涛

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

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

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

打赏作者

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

抵扣说明:

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

余额充值