Java Stream流指南:优雅处理集合数据

文章目录

  • 一、为什么要使用stream流呢?
  • 二、如何获取Stream流?
  • 三、Stream流的中间方法
  • 四、Stream流的终结方法
  • 总结

一、为什么要使用stream流呢?

想必我们在日常编程中,会经常进行数据的处理,我们先来看看没有stram流时,我们的操作方式,我们想要收集姓赵的学生姓名。

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","赵坤","张良","赵雯");
        ArrayList<String> list1 = new ArrayList<>();
        list.forEach(s -> {
            if(s.startsWith("赵")) {
                list1.add(s);
            }
        });
        list1.forEach(s -> System.out.println(s));
    }
}

在这里插入图片描述

这是我们在没有接触到Stream流时的操作方法,虽然使用lambda简化写法了,但还是不够优雅。

下面我使用Stream流的方式来操作一下:

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","赵坤","张良","赵雯");
        list.stream().filter(s -> s.startsWith("赵")).forEach(s -> System.out.println(s));
    }
}

在这里插入图片描述
使用Stream流后的操作是不是比刚开始的操作要优雅许多,大家先不用管这块代码怎么写的,我会在下面一一讲解到。

二、如何获取Stream流?

我们可以简单理解Stream流就是一条流水线。
在这里插入图片描述

Stream流的作用: 结合Lambda表达式,简化集合、数组的操作

Stream流的使用步骤:

  1. 先得到一条Stream流(流水线),并把数据放上去
  2. 利用Stream流中的API进行各种操作(过滤、转换、统计、打印等)
获取方式方法名说明
单列集合default Stream stream()Collection中的默认方法
双列集合无法直接使用stream流,需要先转为单列集合
数组publis static Stream stream(T[] array)Arrays工具类中的静态方法
零散数据public static Stream of(T… values)Stream接口中的静态方法

单列集合:
在这里插入图片描述
我们可以发现单列集合可以直接调用stream方法获取Stream流。
在这里插入图片描述
我们使用下stream流的forEach方法,顾名思义就是打印方法。
在这里插入图片描述
我们可以看到forEach方法的参数是一个函数式接口,所以我们可以使用lambda表达式简化,我们先写一下匿名内部类的写法:

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","赵坤","张良","赵雯");
        Stream<String> stream = list.stream();
        stream.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
    }

然后我们在使用lambda表达式对匿名内部类进行简化:

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","赵坤","张良","赵雯");
        Stream<String> stream = list.stream();
        stream.forEach(s -> System.out.println(s));
    }

在这里插入图片描述
双列集合:
双列集合是无法直接获取stream流的,所以我们需要先将双列集合转换为单列集合,再去进行流式操作。
第一种方式,先将map转为Keyset:

public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<>();
        map.put("四级",425);
        map.put("六级",425);
        map.put("考研",436);
        // 1.第一种获取stream流方式
        map.keySet().stream().forEach(s -> {
            System.out.println(s + ": " + map.get(s));
        });
    }

第二种方式,转为EntrySet:

// 2.第二种获取stream方式
        map.entrySet().stream().forEach(s -> System.out.println(s));

在这里插入图片描述
数组:

public static void main(String[] args) {
        int[] arr = {2,9,3,74,2,1};
        // 使用Arrays中的stream方法获取stream流
        Arrays.stream(arr).forEach(s -> System.out.println(s));
    }

在这里插入图片描述
零散数据:
零散数据使用Stream.of()方法获取stream流:

public static void main(String[] args) {
        Stream.of(1,2,4,3,2,5).forEach(s -> System.out.println(s));
    }

在这里插入图片描述
需要注意的坑:
在这里插入图片描述
我们来看Stream.of()方法的参数是泛型可变参数,那证明可以接受数组数据,我们一起来试一下
在这里插入图片描述
当我们传入的数据是引用数据类型的时候是可以正常操作的,我们再来试试基本数据类型的数组
在这里插入图片描述
当我们传入基本数据类型时,发现打印的是地址,当我们使用Stream.of()方法传入基本数据类型的数组获取stream流时,是将整个数据当作一个元素的

三、Stream流的中间方法

方法作用
Stream filter(Predicate<? super T> predicate)过滤
Stream limit(long maxSize)获取前几个元素
Stream skip(long n)跳过前几个元素
Stream distinct()元素去重,底层使用hashset去重
static Stream concat(Stream a,Stream b)合并a和b两个流为一个流
Stream map(Function<T,R> mapper)转换流中的数据类型

注意点:

  1. 中间方法,返回新的Stream流,原来的Stream流只能使用一次,一般使用链式编程
  2. 修改Stream流中的数据,不会影响原来集合中的数据

filter方法:
我们还是以最开始的例子来讲解
在这里插入图片描述
我们可以看到filiter方法的参数是一个函数式接口,所以我们可以使用lambda表达式简化写法。

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","赵坤","张良","赵雯");
        list.stream().filter(s -> s.startsWith("赵")).forEach(s -> System.out.println(s));
    }
}

而且我们的stream流只能使用一次。
在这里插入图片描述
当我们第二次去使用stream流时,报了IllegalStateException,意思stream流已经关闭

limit方法:
limit方法代表获取前几个元素

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","赵坤","张良","赵雯");
        // 获取前三个元素
        list.stream().limit(3).forEach(s -> System.out.println(s));
    }

在这里插入图片描述
skip方法:
skip方法代表跳过几个元素

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","赵坤","张良","赵雯");
        // 跳过前两个元素
        list.stream().skip(2).forEach(s -> System.out.println(s));
    }

在这里插入图片描述
distinct方法:
distinct方法代表去除重复元素

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","赵子龙","张良","赵雯");
        // 去重数据
        list.stream().distinct().forEach(s -> System.out.println(s));
    }

在这里插入图片描述
我们去看一看distinct底层是如何实现的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们可以看到这个方法内容非常的多,我们可以看到这里是使用HashSet进行去重的,所以我们在使用引用数据类型需要重写equals和hashcode方法

concat方法:
Stream.concat()方法代表合并两个流

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","赵子龙","张良","赵雯");
        ArrayList<String> list1 = new ArrayList<>();
        Collections.addAll(list1,"奥利奥","方便面");
        // 合并两个流对象
        Stream.concat(list.stream(),list1.stream()).forEach(s -> System.out.println(s));
    }

在这里插入图片描述
map方法:
map方法转换流中的数据类型
在这里插入图片描述
默认转换为Object类型,我们可以根据自己的需要进行修改

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"111","222","333");
        list.stream().map(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return Integer.parseInt(s);
            }
        }).forEach(s -> System.out.println(s));
    }

我们可以使用lambda表达式进行简写
在这里插入图片描述

四、Stream流的终结方法

Stream流的终结方法,顾名思义,调用之后就不能再调用Stream流中的其他方法了。

方法作用
void forEach(Consumer action)遍历
long count()统计
toArray()收集流中的数据,放到数组中
collect(Collector collector)收集流中的数据,放到集合中

forEach方法我们这里就不再阐述了,因为我们已经使用的很熟练了,我们来看一下count()方法:
在这里插入图片描述
我们可以看到count()方法返回的是一个long的数值

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","周瑜","张良","赵雯");
        long count = list.stream().count();
        System.out.println(count);
    }

在这里插入图片描述
toArray()方法,将流中的数据放入数组中:
在这里插入图片描述
我们可以看到toArray()方法有两种调用方式,第一种空参,返回Object类型数组

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","周瑜","张良","赵雯");
        Object[] arr = list.stream().toArray();
        System.out.println(Arrays.toString(arr));
    }

在这里插入图片描述
然后我们来看一下传入指定类型的方法
在这里插入图片描述
在这里插入图片描述
我们可以看到toArray传入的参数是一个函数式接口,当中有一个apply方法,形参为value,我们可以理解为流中数据的个数

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","周瑜","张良","赵雯");
        String[] arr = list.stream().toArray(new IntFunction<String[]>() {
            @Override
            public String[] apply(int value) {
                return new String[value];
            }
        });
        System.out.println(Arrays.toString(arr));
    }

我们可以使用lambda表达式进行简写

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","周瑜","张良","赵雯");
        String[] arr = list.stream().toArray(value -> new String[value]);
        System.out.println(Arrays.toString(arr));
    }

在这里插入图片描述
collect方法,收集流中的数据,放到集合当中(List Set Map)。

将流中的数据放到List中:

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","周瑜","张良","赵雯");
        // 将流中的数据放到List中
        List<String> lists = list.stream().collect(Collectors.toList());
        System.out.println(lists);
    }

在这里插入图片描述
将流中的数据放到Set中:

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙","猪大肠","周瑜","张良","赵雯");
        // 将流中的数据放到List中
        Set<String> set = list.stream().collect(Collectors.toSet());
        System.out.println(set);
    }

在这里插入图片描述
那么将流数据放到List和Set有什么区别呢?放入Set会进行去重操作

将流中的数据放到Map中:
我们再将流数据转为Map之前,我们需要弄情况,用什么做key,用什么做value
在这里插入图片描述
我们需要指定具体的key和value的规则,我们来看看具体的参数
在这里插入图片描述
在这里插入图片描述
我们可以发现是函数式接口。
在这里插入图片描述
s就是我们流里面的数据,两个匿名内部类分别返回的是key和value

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙-18","猪大肠-21","周瑜-12","张良-16","赵雯-17");
        Map<String, Integer> map = list.stream().collect(Collectors.toMap(new Function<String, String>() {
            @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("-")[1]);
            }
        }));
        System.out.println(map);
    }

在这里插入图片描述
我们使用lambda表达式进行简化:

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"赵子龙-18","猪大肠-21","周瑜-12","张良-16","赵雯-17");
        Map<String, Integer> map = list.stream().collect(Collectors.toMap(s -> s.split("-")[0],s -> Integer.parseInt(s.split("-")[1])));
        System.out.println(map);
    }

在这里插入图片描述

总结

1.Stream流的作用:
结合了Lambda表达式,简化集合、数组的操作

2.Stream流的使用步骤:

  • 获取Stream流对象
  • 使用中间方法处理数据
  • 使用终结方法处理数据

3.如何获取Stream流对象

  • 单列集合:Collection中默认的stream方法
  • 双列集合:不能直接获取,需要先转为单列集合去获取
  • 数据:Arrays工具类中的stream静态方法
  • 零散数据:Stream接口中的of静态方法

4.常见方法

  • 中间方法:filter、limit、skip、distinct、concat、map
  • 终结方法:forEach、count、toArray、collect

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/414517.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从零开始学习Netty - 学习笔记 -Netty入门-ChannelFuture

5.2.2.Channel Channel 的基本概念 在 Netty 中&#xff0c;Channel 是表示网络传输的开放连接的抽象。它提供了对不同种类网络传输的统一视图&#xff0c;比如 TCP 和 UDP。 Channel 的生命周期 Channel 的生命周期包括创建、激活、连接、读取、写入和关闭等阶段。Netty 中…

CGI程序与ShellShock漏洞

CGI是什么&#xff1f; CGI&#xff08;通用网关接口&#xff0c;Common Gateway Interface&#xff09;程序是一种用于在Web服务器上执行动态内容的技术。与服务器上普通的后端代码相比&#xff0c;CGI程序有几个区别&#xff1a; 执行环境&#xff1a; CGI程序在服务器上作为…

js中Symbol的理解与应用

文章目录 一、Symbol特性1.1 不支持语法new Symbol()1.2 唯一性1.3 不与其他值隐式转换1.4 不可枚举1.5 类型为symbol 二、Symbol常见方法2.1 Symbol.toStringTag2.2 Symbol.iterator2.3 Symbol.for() 三、Symbol应用 在JavaScript中&#xff0c;Symbol 是一种基本数据类型&…

el-table 多选表格存在分页,编辑再次操作勾选会丢失原来选中的数据

el-table表格多选时&#xff0c;只需要添加type"selection"&#xff0c; row-key及selection-change&#xff0c;如果存在分页时需要加上reserve-selection&#xff0c;这里就不写具体的实现方法了&#xff0c;可以查看我之前的文章&#xff0c;这篇文章主要说一下存…

智能指针(C++)

目录 一、智能指针是什么 二、为什么需要智能指针 三、智能指针的使用和原理 3.1、RALL 3.2 智能指针的原理 3.3、智能指针的分类 3.3.1、auto_ptr 3.3.2、unique_ptr 3.3.3、shared_ptr 3.2.4、weak_ptr 一、智能指针是什么 在c中&#xff0c;动态内存的管理式通过一…

VPX基于全国产飞腾FT-2000+/64核+复旦微FPGA的计算刀片

6U VPX计算板 产品简介 产品特点 飞腾计算平台&#xff0c;国产化率100% VPX-MPU6902是一款基于飞腾FT-2000/64核的计算刀片&#xff0c;主频2.2GHz&#xff0c;负责业务数据流的管控和调度。搭配自带独立显示芯片的飞腾X100芯片&#xff0c;可用于于各类终端及服务器类应用场…

Spring与SpringBoot入门

Spring入门 要使用Spring最起码需要引入两个依赖: <!-- Spring Core&#xff08;核心&#xff09; --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.3.20</version>…

小白水平理解面试经典题目LeetCode 655. Print Binary Tree【Tree】

655 打印二叉树 一、小白翻译 给定二叉树的 root &#xff0c;构造一个 0 索引的 m x n 字符串矩阵 res 来表示树的格式化布局。格式化布局矩阵应使用以下规则构建&#xff1a; 树的高度为 height &#xff0c;行数 m 应等于 height 1 。 列数 n 应等于​​xheight1​​ - …

爆火的1分钟声音克隆GPT-SoVITS项目 linux系统 ubuntu22.04安装2天踩坑教程

原项目地址&#xff1a;https://github.com/RVC-Boss/GPT-SoVITS 1分钟素材&#xff0c;最后出来的效果确实不错。 1. cuda环境安装 cuda环境准备 根据项目要求在cuda11.8和12.3都测试了通过。我这里是用cuda11.8 cuda11.8安装教程&#xff1a; ubuntu 22.04 cuda多版本和…

【软件测试】--功能测试4-html介绍

1.1 前端三大核心 html:超文本标记语言&#xff0c;由一套标记标签组成 标签&#xff1a; 单标签&#xff1a;<标签名 /> 双标签:<标签名></标签名> 属性&#xff1a;描述某一特征 示例:<a 属性名"属性值"> 1.2 html骨架标签 <!DOC…

蓝桥杯第十四届电子类单片机组决赛程序设计

目录 前言 单片机资源数据包_2023&#xff08;点击下载&#xff09; 一、第十四届比赛题目 1.比赛题目 2.题目解读 1&#xff09;任务要求 2&#xff09;注意事项 二、显示功能实现 1.关于高位为0时数码管熄灭功能的实现 2.关于显示小数位的处理 3.关于“校准值”的…

某查查首页瀑布流headers加密

目标网站&#xff1a; 某查查 对目标网站分析发现 红框内的参数和值都是加密的&#xff0c;是根据算法算出来的&#xff0c;故进行逆向分析。 由于没有固定参数名&#xff0c;只能通过搜索headers&#xff0c;在搜索的位置上打上断点&#xff0c;重新请求。 断点在此处断住&a…

【计算机】本科考研还是就业?

其实现在很多计算机专业的学生考研&#xff0c;也是无奈的选择 技术发展日新月异&#xff0c;而在本科阶段&#xff0c;大家学着落后的技术&#xff0c;出来找工作自然会碰壁。而且现在用人单位的门槛越来越高&#xff0c;学历默认研究生起步&#xff0c;面试一般都是三轮起步…

循序渐进丨MogDB / openGauss 如何实现自增主键

概述 自增主键是我们在设计数据库表结构时经常使用的主键生成策略&#xff0c;主键的生成可以完全依赖数据库&#xff0c;无需人为干预&#xff0c;在新增数据的时候&#xff0c;我们只需要将主键的值设置为default&#xff0c;数据库就会为我们自动生成一个主键值。 MySQL 主键…

基于沁恒微 ch643q 多通道采集 adc 驱动层实现

一、代码 #include "main.h"/********************************************************************** fn ADC_Function_Init** brief Initializes ADC collection.** return none*/ void ADC_Function_Init(void) {ADC_InitTypeDef ADC_InitStructure …

【Go 快速入门】协程 | 通道 | select 多路复用 | sync 包

文章目录 前言协程goroutine 调度使用 goroutine 通道无缓冲通道有缓冲通道单向通道 select 多路复用syncsync.WaitGroupsync.Mutexsync.RWMutexsync.Oncesync.Map 项目代码地址&#xff1a;05-GoroutineChannelSync 前言 Go 1.22 版本于不久前推出&#xff0c;更新的新特性可…

LoRa技术在智能气象监测中的应用与解决方案分享

LoRa技术在智能气象监测领域的应用具有广泛的前景&#xff0c;通过LoRa技术可以实现对气象数据的远程采集、传输和监测&#xff0c;为气象行业提供更加智能化和高效的解决方案。以下将探讨LoRa技术在智能气象监测中的应用与解决方案分享。 首先&#xff0c;LoRa技术可以用于连…

python|闲谈2048小游戏和数组的旋转及翻转和转置

目录 2048 生成数组 n阶方阵 方阵旋转 顺时针旋转 逆时针旋转 mxn矩阵 矩阵旋转 测试代码 测试结果 翻转和转置 2048 《2048》是一款比较流行​的数字游戏​&#xff0c;最早于2014年3月20日发行。原版2048由Gabriele Cirulli首先在GitHub上发布&#xff0c;后被移…

【C语言】数据存储篇,内存中的数据存储----C语言整型,浮点数的数据在内存中的存储以及大小端字节序【图文详解】

欢迎来CILMY23的博客喔&#xff0c;本篇为​【C语言】数据存储篇&#xff0c;内存中的数据存储----C语言整型&#xff0c;浮点数的数据在内存中的存储以及大小端字节序【图文详解】&#xff0c;感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞关注收藏。 前言 C语…

GIS之深度学习02:Anaconda2019版本安装(py38)

Anaconda是一个专注于数据科学和机器学习的开源发行版&#xff0c;内置了丰富的工具和库&#xff0c;包括Python解释器、NumPy、SciPy、Pandas、Scikit-learn、TensorFlow等&#xff0c;使用户能够轻松进行科学计算和数据分析。其强大的包管理器conda简化了软件包的安装和环境管…