集合进阶8——Stream流

一、Stream流

1.1 初体验

java 复制代码
package com.lkbhua.StreamDemo;

import java.util.ArrayList;

public class demo1 {
    public static void main(String[] args) {
        /*
            创建集合添加元素,完成以下需求:
            1、把所有以"张"开头的元素存储到新集合当中
            2、把"张"开头的,长度为3的元素存储到新集合当中
            3、遍历并打印最终的结果
        */
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("张三");
        list1.add("张三丰");
        list1.add("张无忌");
        list1.add("李四");
        list1.add("马思维");

        // 不用stream流的方法
        /*
        // 1、把所有以"张"开头的元素存储到新集合当中
        ArrayList<String> list2 = new ArrayList<>();

        for(String name : list1){
            if(name.startsWith("张")){
                list2.add(name);
            }
        }
        System.out.println(list2); // [张三, 张三丰, 张无忌]

        // 2、把"张"开头的,长度为3的元素存储到新集合当中
        ArrayList<String> list3 = new ArrayList<>();
        for(String name : list2){
            if(name.length() == 3){
                list3.add(name);
            }
        }
        System.out.println(list3); // [张三, 张无忌]

        // 3、遍历并打印最终的结果
        for(String name : list3){
            System.out.println(name);
        }
        */

        System.out.println("-----------------");

        // 用stream流
        list1.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3).forEach(name -> System.out.println(name));
    }
}

1.1.1什么是 Stream 流?

Stream 是 Java 中对数据源 (如集合、数组等)进行一系列操作的抽象表示。它不是数据结构,也不存储数据,而是对数据进行计算的一系列操作管道。

特点:

  • 不存储数据:只对数据源进行操作。
  • 函数式编程风格:使用 Lambda 表达式。
  • 惰性求值(Lazy Evaluation):中间操作不会立即执行,只有遇到终结操作才会真正执行。
  • 不可重复使用:一个 Stream 只能被消费一次,再次使用会抛出异常。
  • 支持并行处理 :可通过 parallel() 转为并行流提升性能。

1.2 Stream流的思想

Stream 的核心思想是"流水线(Pipeline)":

  1. 源头(Source):提供原始数据(集合、数组、I/O 等)。
  2. 中间操作(Intermediate Operations):对数据进行转换、过滤等,返回新的 Stream(可链式调用)。
  3. 终结操作(Terminal Operation):触发整个流水线执行,产生结果或副作用(如打印、收集等)。

类似工厂流水线:原材料 → 加工 → 成品

首先把要操作的数据放到流水线上,进行过滤操作,其他数据就舍弃不要,最后进行输出操作。

1.3 使用步骤

1.3.1 如何放到流水线上

注意:

① 双列集合不能直接转成Stream流,需要通过keySet()或者entrySet()转换成单列集合,才能获取Stream流;

② 一堆零散的数据可以通过Stream接口中的静态方法放到流水线上,但是这些数据需要同种数据类型的。

java 复制代码
package com.lkbhua.StreamDemo;

import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;

public class demo2 {
    public static void main(String[] args) {
        /*  Stream流的使用步骤1:
            如何将数据放到Stream流上?
            单列集合        default Stream<E> stream()                      Collection中的默认方法
            双列集合        无                                              无法直接使用stream流
            数组           public static <T> Stream<T> stream(T[] array)   Arrays工具类中的静态方法
            一堆零散的数据   public static <T> Stream<T> of(T...values)      Stream接口中的静态方法
        */

        // 1、单列集合获取Stream流
        ArrayList<String> list1 = new ArrayList<>();
        Collections.addAll(list1, "a", "b", "c", "d", "e", "f");
        /*// 获取Stream流,并且把数据放到Stream流中
        Stream<String> stream1 = list1.stream();
        // 使用终结方法遍历
        stream1.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                // s: Stream流中的数据
                System.out.println(s);
            }
        });
        */
        list1.stream().forEach(s -> System.out.println(s));

    }
    public static void method2(){
        // 2、双列集合获取Stream流
        // 无法直接创建,需要转换成单列集合

        // 创建双列集合
        HashMap<String,String> map = new HashMap<>();
        map.put("1", "a");
        map.put("2", "b");
        map.put("3", "c");
        map.put("4", "d");

        // case1:通过键或者值获取stream流
        Stream<String> stream = map.values().stream();
        Stream<String> stream1 = map.keySet().stream();
        // case2:通过键值对获取stream流
        Stream<Map.Entry<String, String>> stream2 = map.entrySet().stream();

    }

    public static void method3(){
        // 3、数组获取Stream流
        // 基本数据类型
        int[] arr = {1,2,3,4,5,6,7,8,9,10};
        // 获取Stream流
        Arrays.stream(arr).forEach(s -> System.out.println(s));

        // 引用数据类型
        String[] arr1 = {"a","b","c","d","e","f"};
        Arrays.stream(arr1).forEach(s -> System.out.println(s));
    }

    public static void method4(){
        // 4、一堆零散的数据获取Stream流
        // 基本数据类型
        Stream<String> stream = Stream.of("a", "b", "c", "d", "e", "f");
        stream.forEach(s -> System.out.println(s));

        // 引用数据类型
        Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6);
        stream1.forEach(s -> System.out.println(s));
    }
}

1.3.2 中间方法

中间方法返回一个新的 Stream,支持链式调用,不会立即执行

✅ 所有中间操作都是惰性的,只有遇到终结操作才执行。

java 复制代码
package com.lkbhua.StreamDemo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.Predicate;

public class demo3 {
    public static void main(String[] args) {
        /* Stream流的中间方法1
           filter           过滤
           limit            获取前几个元素
           skip             跳过前几个元素

           注意1:中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
           注意2:修改Stream流中的数据,不会影响原来的集合或者数组中的数据
        */

        ArrayList<String> list = new ArrayList<String>();
        Collections.addAll(list, "a", "b", "c", "d", "e", "f");
        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                // true: 表示当前的值留下
                // false: 表示当前的值去掉
                if(s.equals("a") || s.equals("c") || s.equals("e")){
                    return true;
                }
                return false;
            }
        }).forEach(s -> System.out.println(s));
        System.out.println("----------------");

        list.stream().
                filter(s -> s.equals("a") || s.equals("c") || s.equals("e")).
                forEach(s -> System.out.println(s));
        // 这样可以提高链式编程的阅读性

        System.out.println("----------------");
        System.out.println(list);

        // limit获取前几个元素
        list.stream().
                limit(3).
                forEach(s -> System.out.println(s));
        System.out.println("----------------");

        // skip跳过前几个元素
        list.stream().
                skip(3).
                forEach(s -> System.out.println(s));

    }
}

1.3.3 终结方法

处理完的数据,放到集合或者数组当中存储。终结方法会触发流的执行,并产生结果或副作用。执行后流被关闭,不能复用。

⚠️ 终结操作只能有一个!

java 复制代码
package com.lkbhua.StreamDemo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.function.IntFunction;

public class demo6 {
    public static void main(String[] args) {
        /*  stream的终结方法
            void forEach(Consumer action)       遍历
            long count()                        统计数量
            toArray()                           收集流中的数据,放到数组当中

        */

        ArrayList<String> list = new ArrayList<String>();
        Collections.addAll(list, "a", "b", "c", "d", "e", "f");

        // 遍历
        // Consumer的泛型:表示流中数据的类型
        // accept方法的形参s:依次表示流里面的每一个数据
        // 方法体:对每一个数据进行操作(这里是:打印)
        list.stream().forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        list.stream().forEach(s -> System.out.println(s));
        System.out.println("----------------");

        ArrayList<String> list1 = new ArrayList<String>();
        Collections.addAll(list1, "a", "b", "c", "d", "e", "f");
        // 统计数量
        long count = list1.stream().count();
        System.out.println(count);
        System.out.println("----------------");

        ArrayList<String> list2 = new ArrayList<String>();
        Collections.addAll(list2, "a", "b", "c", "d", "e", "f");
        // 收集流中的数据,放到数组当中
        // Object[] array = list1.stream().toArray();

        // IntFunction的泛型:具体类型的数组
        // apply的形参:流中数据的个数,要跟数组的长度保持一致
        // 返回值: 具体类型的数组
        // 方法体:就是创建数组


        // toArray方法的参数的作用:负责创建一个指定类型的数组
        // toArray方法的底层:会依次得到流里面的每一个数据,并把数据放到数组当中
        // toArray方法的返回值:是一个装着流里面所有数据的数组
        String[] array = list.stream().toArray(new IntFunction<String[]>() {
            @Override
            public String[] apply(int value) {
                return new String[value];
            }
        });
        System.out.println(Arrays.toString( array));
    }
}

1.3.4 收集方法

collect(Collector) 是最强大的终结操作之一,用于将流结果收集到集合、字符串、分组等。

常用 Collectors:

方法 用途
toList() 收集为 List
toSet() 收集为 Set
toMap(keyMapper, valueMapper) 收集为 Map
joining() / joining(delimiter) 拼接字符串
groupingBy(classifier) 分组(Map<K, List>)
partitioningBy(predicate) 分区(Map<Boolean, List>)
summingInt/mapping/averagingDouble 聚合统计
java 复制代码
package com.lkbhua.StreamDemo;

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

public class demo7 {
    public static void main(String[] args) {
        /*
            collect(Collector collector)     收集流中的数据,放到集合当中(List、Map、Set)

            注意点:
                如果我们要收集到Map集合当中,键不能重复,否则会报错
        */

        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"a-男-1","b-男-2","c-女-3","d-男-4","e-女-5","f-女-6","g-男-7","h-女-8");

        // 收集到List集合当中
        // 需求:找出里面的男性并且收集
        // 能用固定数据去调用equals的方法就用固定数据去调用
        List<String> newlist1 = list.stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toList());
        System.out.println(newlist1);
        System.out.println("----------------");

        // 收集到Set集合当中
        // Set集合:无序,不能重复
        // 需求:找出里面的男性并且收集
        // 能用固定数据去调用equals的方法就用固定数据去调用
        Set<String> newSet2 = list.stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toSet());
        System.out.println(newSet2);
        System.out.println("----------------");

        // 收集到Map集合当中
        // 注意:谁做为键,谁做为值
        // 键:字母,值:数字
        Map<String,Integer> newMap3 = list.stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toMap(new Function<String, String>() {
                    /*
                    *   toMap:参数一:键的生成规则,
                    *         参数二:值的生成规则
                    *   参数一:
                    *        Function泛型一: 表示流中每一个数据的类型
                    *        Function泛型二: 表示Map集合中键的数据类型
                    *   方法apply的形参:依次表示流里的每一个数据
                    *           方法体:生成键
                    *           返回值:已经生成的键
                    *   参数二:
                    *        Function泛型一: 表示流中每一个数据的类型
                    *        Function泛型二: 表示Map集合中值的数据类型
                    *   方法apply的形参:依次表示流里的每一个数据
                    *           方法体:生成值
                    *           返回值:已经生成的值
                    * */
                    @Override
                    public String apply(String s) {
                        return s.split("-")[0];
                    }
                },
                        new Function<String, Integer>() {
                            @Override
                            public Integer apply(String s) {
                                return Integer.parseInt(s.split("-")[2]);
                            }
                        }));
        System.out.println(newMap3);

        System.out.println("----------------");
        Map<String,Integer> newMap4 = list.stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toMap(
                        s -> s.split("-")[0],
                        s -> Integer.parseInt(s.split("-")[2])));
        System.out.println(newMap4);
        System.out.println("----------------");
    }
}
相关推荐
20岁30年经验的码农2 小时前
Java Elasticsearch 实战指南
java·开发语言·elasticsearch
okseekw2 小时前
Java 中的注释与关键字的初步学习
java
爱学java的ptt2 小时前
jvm笔记
jvm·笔记
雾岛听蓝2 小时前
C++ 类和对象(一):从概念到实践,吃透类的核心基础
开发语言·c++·经验分享·笔记
luv_sw2 小时前
JavaSE-面向对象-构造器
java
okseekw2 小时前
Java 中的类型转换:结合实战代码深入解析
java
CoderYanger2 小时前
优选算法-优先级队列(堆):75.数据流中的第K大元素
java·开发语言·算法·leetcode·职场和发展·1024程序员节
luv_sw2 小时前
JavaSE-面向对象-抽象类和接口
java
TracyCoder1232 小时前
MySQL 实战宝典(八):Java后端MySQL分库分表工具解析与选型秘籍
java·开发语言·mysql