Java8中Stream流API最佳实践Lambda表达式使用示例
文章目录
- 一、创建流
- 二、中间操作和收集操作
- 筛选 filter
- 去重distinct
- 截取
- 跳过
- 映射
- 合并多个流
- 是否匹配任一元素:anyMatch
- 是否匹配所有元素:allMatch
- 是否未匹配所有元素:noneMatch
- 获取任一元素findAny
- 获取第一个元素findFirst
- 归约
- 数值流的使用
- 中间操作和收集操作总结
- Collector 收集
- 归约
- 一般性归约
- 汇总
- 分组
- 多级分组
- 转换类型
- 数据分区
- 并行流

stream在使用中主要分为以上图示阶段,接下来将详细展示java8中stream api的具体的实践使用案例。
一、创建流
在使用流之前,首先需要拥有一个数据源,并通过StreamAPI提供的一些方法获取该数据源的流对象。数据源可以有多种形式:
1. 集合
这种数据源较为常用,通过stream()方法即可获取流对象:
List<Person> list = new ArrayList<Person>();
Stream<Person> stream = list.stream();
2. 数组
通过Arrays类提供的静态函数stream()获取数组的流对象:
String[] names = {"chaimm","peter","john"};
Stream<String> stream = Arrays.stream(names);
3. 值
直接将几个值变成流对象:
Stream<String> stream = Stream.of("chaimm","peter","john");
4. 文件
try(Stream lines = Files.lines(Paths.get(“文件路径名”),Charset.defaultCharset())){//可对lines做一些操作
}catch(IOException e){
}
5. iterator
创建无限流
Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
PS:Java7简化了IO操作,把打开IO操作放在try后的括号中即可省略关闭IO的代码。
二、中间操作和收集操作
筛选 filter
filter 函数接收一个Lambda表达式作为参数,该表达式返回boolean,在执行过程中,流将元素逐一输送给filter,并筛选出执行结果为true的元素。
如,筛选出所有学生:
List<Person> result = list.stream().filter(Person::isStudent).collect(toList());
去重distinct
去掉重复的结果:
List<Person> result = list.stream().distinct().collect(toList());
截取
截取流的前N个元素:
List<Person> result = list.stream().limit(3).collect(toList());
跳过
跳过流的前n个元素:
List<Person> result = list.stream().skip(3).collect(toList());
映射
对流中的每个元素执行一个函数,使得元素转换成另一种类型输出。流会将每一个元素输送给map函数,并执行map中的Lambda表达式,最后将执行结果存入一个新的流中。
如,获取每个人的姓名(实则是将Perosn类型转换成String类型):
List<Person> result = list.stream().map(Person::getName).collect(toList());
合并多个流
例:列出List中各不相同的单词,List集合如下:
List<String> list = new ArrayList<String>();
list.add("I am a boy");
list.add("I love the girl");
list.add("But the girl loves another girl");
思路如下:
首先将list变成流:
list.stream();
按空格分词:
list.stream().map(line->line.split(" "));
分完词之后,每个元素变成了一个String[]数组。
将每个 String[]
变成流:
list.stream().map(line->line.split(" ")).map(Arrays::stream)
此时一个大流里面包含了一个个小流,我们需要将这些小流合并成一个流。
将小流合并成一个大流:用 flatMap
替换刚才的 map
list.stream().map(line->line.split(" ")).flatMap(Arrays::stream)
去重
list.stream().map(line->line.split(" ")).flatMap(Arrays::stream).distinct().collect(toList());
是否匹配任一元素:anyMatch
anyMatch用于判断流中是否存在至少一个元素满足指定的条件,这个判断条件通过Lambda表达式传递给anyMatch,执行结果为boolean类型。
如,判断list中是否有学生:
boolean result = list.stream().anyMatch(Person::isStudent);
是否匹配所有元素:allMatch
allMatch用于判断流中的所有元素是否都满足指定条件,这个判断条件通过Lambda表达式传递给anyMatch,执行结果为boolean类型。
如,判断是否所有人都是学生:
boolean result = list.stream().allMatch(Person::isStudent);
是否未匹配所有元素:noneMatch
noneMatch与allMatch恰恰相反,它用于判断流中的所有元素是否都不满足指定条件:
boolean result = list.stream().noneMatch(Person::isStudent);
获取任一元素findAny
findAny能够从流中随便选一个元素出来,它返回一个Optional类型的元素。
Optional<Person> person = list.stream().findAny();
获取第一个元素findFirst
Optional<Person> person = list.stream().findFirst();
归约
归约是将集合中的所有元素经过指定运算,折叠成一个元素输出,如:求最值、平均数等,这些操作都是将一个集合的元素折叠成一个元素输出。
在流中,reduce函数能实现归约。
reduce函数接收两个参数:
- 初始值
- 进行归约操作的Lambda表达式
元素求和:自定义Lambda表达式实现求和
例:计算所有人的年龄总和
@Test
public void contextLoads() {List<Person> list = new ArrayList<>();list.add(new Person().setAge(20));list.add(new Person().setAge(25));int age = list.stream().map(Person::getAge).reduce(0, Integer::sum);System.out.println(age);
}
@Data
@Accessors(chain = true)
class Person {private int age;
}
- reduce的第一个参数表示初试值为0;
- reduce的第二个参数为需要进行的归约操作,它接收一个拥有两个参数的Lambda表达式,reduce会把流中的元素两两输给Lambda表达式,最后将计算出累加之和。
元素求和:使用Integer.sum函数求和
上面的方法中我们自己定义了Lambda表达式实现求和运算,如果当前流的元素为数值类型,那么可以使用Integer提供了sum函数代替自定义的Lambda表达式,如:
int age = list.stream().reduce(0, Integer::sum);
Integer类还提供了 min
、max
等一系列数值操作,当流中元素为数值类型时可以直接使用。
数值流的使用
采用reduce进行数值操作会涉及到基本数值类型和引用数值类型之间的装箱、拆箱操作,因此效率较低。
当流操作为纯数值操作时,使用数值流能获得较高的效率。
将普通流转换成数值流
StreamAPI提供了三种数值流:IntStream、DoubleStream、LongStream,也提供了将普通流转换成数值流的三种方法:mapToInt、mapToDouble、mapToLong。
如,将Person中的age转换成数值流:
IntStream stream = list.stream().mapToInt(Person::getAge);
数值计算
每种数值流都提供了数值计算函数,如max、min、sum等。如,找出最大的年龄:
OptionalInt maxAge = list.stream().mapToInt(Person::getAge).max();
由于数值流可能为空,并且给空的数值流计算最大值是没有意义的,因此max函数返回OptionalInt,它是Optional的一个子类,能够判断流是否为空,并对流为空的情况作相应的处理。
此外,mapToInt、mapToDouble、mapToLong进行数值操作后的返回结果分别为:OptionalInt、OptionalDouble、OptionalLong
中间操作和收集操作总结
操作 | 类型 | 返回类型 | 使用的类型/函数式接口 | 函数描述符 |
---|---|---|---|---|
filter | 中间 | Stream<T> | Predicate<T> | T -> boolean |
distinct | 中间 | Stream<T> | ||
skip | 中间 | Stream<T> | long | |
map | 中间 | Stream<R> | Function<T, R> | T -> R |
flatMap | 中间 | Stream<R> | Function<T, Stream<R>> | T -> Stream<R> |
limit | 中间 | Stream<T> | long | |
sorted | 中间 | Stream<T> | Comparator<T> | (T, T) -> int |
anyMatch | 终端 | boolean | Predicate<T> | T -> boolean |
noneMatch | 终端 | boolean | Predicate<T> | T -> boolean |
allMatch | 终端 | boolean | Predicate<T> | T -> boolean |
findAny | 终端 | Optional<T> | ||
findFirst | 终端 | Optional<T> | ||
forEach | 终端 | void | Consumer<T> | T -> void |
collect | 终端 | R | Collector<T, A, R> | |
reduce | 终端 | Optional<T> | BinaryOperator<T> | (T, T) -> T |
count | 终端 | long |
Collector 收集
收集器用来将经过筛选、映射的流进行最后的整理,可以使得最后的结果以不同的形式展现。
collect
方法即为收集器,它接收 Collector
接口的实现作为具体收集器的收集方法。
Collector
接口提供了很多默认实现的方法,我们可以直接使用它们格式化流的结果;也可以自定义 Collector
接口的实现,从而定制自己的收集器。
归约
流由一个个元素组成,归约就是将一个个元素“折叠”成一个值,如求和、求最值、求平均值都是归约操作。
一般性归约
若你需要自定义一个归约操作,那么需要使用 Collectors.reducing
函数,该函数接收三个参数:
- 第一个参数为归约的初始值
- 第二个参数为归约操作进行的字段
- 第三个参数为归约操作的过程
汇总
Collectors类专门为汇总提供了一个工厂方法:Collectors.summingInt
。
它可接受一 个把对象映射为求和所需int的函数,并返回一个收集器;该收集器在传递给普通的 collect
方法后即执行我们需要的汇总操作。
分组
数据分组是一种更自然的分割数据操作,分组就是将流中的元素按照指定类别进行划分,类似于SQL语句中的 GROUPBY
。
多级分组
多级分组可以支持在完成一次分组后,分别对每个小组再进行分组。
使用具有两个参数的 groupingBy
重载方法即可实现多级分组。
- 第一个参数:一级分组的条件
- 第二个参数:一个新的
groupingBy
函数,该函数包含二级分组的条件
Collectors 类的静态工厂方法
工厂方法 | 返回类型 | 用途 | 示例 |
---|---|---|---|
toList | List<T> | 把流中所有项目收集到一个 List | List<Project> projects = projectStream.collect(toList()); |
toSet | Set<T> | 把流中所有项目收集到一个 Set,删除重复项 | Set<Project> projects = projectStream.collect(toSet()); |
toCollection | Collection<T> | 把流中所有项目收集到给定的供应源创建的集合 | Collection<Project> projects = projectStream.collect(toCollection(), ArrayList::new); |
counting | Long | 计算流中元素的个数 | long howManyProjects = projectStream.collect(counting()); |
summingInt | Integer | 对流中项目的一个整数属性求和 | int totalStars = projectStream.collect(summingInt(Project::getStars)); |
averagingInt | Double | 计算流中项目 Integer 属性的平均值 | double avgStars = projectStream.collect(averagingInt(Project::getStars)); |
summarizingInt | IntSummaryStatistics | 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值 | IntSummaryStatistics projectStatistics = projectStream.collect(summarizingInt(Project::getStars)); |
joining | String | 连接对流中每个项目调用 toString 方法所生成的字符串 | String shortProject = projectStream.map(Project::getName).collect(joining(", ")); |
maxBy | Optional<T> | 按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty() | Optional<Project> fattest = projectStream.collect(maxBy(comparingInt(Project::getStars))); |
minBy | Optional<T> | 按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty() | Optional<Project> fattest = projectStream.collect(minBy(comparingInt(Project::getStars))); |
reducing | 归约操作产生的类型 | 从一个作为累加器的初始值开始,利用 BinaryOperator 与流中的元素逐个结合,从而将流归约为单个值 | int totalStars = projectStream.collect(reducing(0, Project::getStars, Integer::sum)); |
collectingAndThen | 转换函数返回的类型 | 包含另一个收集器,对其结果应用转换函数 | int howManyProjects = projectStream.collect(collectingAndThen(toList(), List::size)); |
groupingBy | Map<K, List<T>> | 根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键 | Map<String,List<Project>> projectByLanguage = projectStream.collect(groupingBy(Project::getLanguage)); |
partitioningBy | Map<Boolean,List<T>> | 根据对流中每个项目应用断言的结果来对项目进行分区 | Map<Boolean,List<Project>> vegetarianDishes = projectStream.collect(partitioningBy(Project::isVegetarian)); |
转换类型
有一些收集器可以生成其他集合。比如前面已经见过的 toList
,生成了 java.util.List
类的实例。
还有 toSet
和 toCollection
,分别生成 Set
和 Collection
类的实例。
到目前为止, 我已经讲了很多流上的链式操作,但总有一些时候,需要最终生成一个集合——比如:
- 已有代码是为集合编写的,因此需要将流转换成集合传入;
- 在集合上进行一系列链式操作后,最终希望生成一个值;
- 写单元测试时,需要对某个具体的集合做断言。
使用 toCollection
,用定制的集合收集元素
stream.collect(toCollection(TreeSet::new));
还可以利用收集器让流生成一个值。 maxBy
和 minBy
允许用户按某种特定的顺序生成一个值。
数据分区
分区是分组的特殊情况:由一个断言(返回一个布尔值的函数)作为分类函数,它称分区函数。
分区函数返回一个布尔值,这意味着得到的分组 Map
的键类型是 Boolean
,于是它最多可以分为两组: true是一组,false是一组。
分区的好处在于保留了分区函数返回true或false的两套流元素列表。
并行流
并行流就是一个把内容分成多个数据块,并用不不同的线程分别处理每个数据块的流。最后合并每个数据块的计算结果。
将一个顺序执行的流转变成一个并发的流只要调用 parallel()
方法
public static long parallelSum(long n){return Stream.iterate(1L, i -> i +1).limit(n).parallel().reduce(0L,Long::sum);
}
将一个并发流转成顺序的流只要调用 sequential()
方法
stream.parallel().filter(...).sequential().map(...).parallel().reduce();
这两个方法可以多次调用,只有最后一个调用决定这个流是顺序的还是并发的。
并发流使用的默认线程数等于你机器的处理器核心数。
通过这个方法可以修改这个值,这是全局属性。
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "12");
并非使用多线程并行流处理数据的性能一定高于单线程顺序流的性能,因为性能受到多种因素的影响。
如何高效使用并发流的一些建议:
- 如果不确定, 就自己测试。
- 尽量使用基本类型的流 IntStream, LongStream, DoubleStream
- 有些操作使用并发流的性能会比顺序流的性能更差,比如limit,findFirst,依赖元素顺序的操作在并发流中是极其消耗性能的。findAny的性能就会好很多,应为不依赖顺序。
- 考虑流中计算的性能(Q)和操作的性能(N)的对比, Q表示单个处理所需的时间,N表示需要处理的数量,如果Q的值越大, 使用并发流的性能就会越高。
- 数据量不大时使用并发流,性能得不到提升。
- 考虑数据结构:并发流需要对数据进行分解,不同的数据结构被分解的性能时不一样的。
流的数据源和可分解性
源 | 可分解性 |
---|---|
ArrayList | 非常好 |
LinkedList | 差 |
IntStream.range | 非常好 |
Stream.iterate | 差 |
HashSet | 好 |
TreeSet | 好 |
流的特性以及中间操作对流的修改都会对数据对分解性能造成影响。 比如固定大小的流在任务分解的时候就可以平均分配,但是如果有filter操作,那么流就不能预先知道在这个操作后还会剩余多少元素。
考虑终端操作的性能:如果终端操作在合并并发流的计算结果时的性能消耗太大,那么使用并发流提升的性能就会得不偿失。
相关文章:

Java8中Stream流API最佳实践Lambda表达式使用示例
文章目录 一、创建流二、中间操作和收集操作筛选 filter去重distinct截取跳过映射合并多个流是否匹配任一元素:anyMatch是否匹配所有元素:allMatch是否未匹配所有元素:noneMatch获取任一元素findAny获取第一个元素findFirst归约数值流的使用中…...
构建Helm chart和chart使用管道与函数简介
目录 一.创建helm chart(以nginx为例) 1.通过create去创建模板 2.查看模板下的文件 3.用chart模版安装nginx 二.版本更新和回滚问题 1.使用upgrade -f values.yaml或者命令行--set来设置 2.查看历史版本并回滚 三.helm模板内管道和函数 1.defau…...
深入理解OnCalculate函数的运行机制
文章目录 一、学习 OnCalculate 函数的运行原理的意义二、OnCalculate 函数原型三、OnCalculate 函数在MT4与MT5区别四、OnCalculate 函数的运行原理 一、学习 OnCalculate 函数的运行原理的意义 OnCalculate函数是MQL语言中的一个重要函数,它用于计算技术指标的值。…...

快速从0-1完成聊天室开发——环信ChatroomUIKit功能详解
聊天室是当下泛娱乐社交应用中最经典的玩法,通过调用环信的 IM SDK 接口,可以快速创建聊天室。如果想根据自己业务需求对聊天室应用的 UI界面、弹幕消息、礼物打赏系统等进行自定义设计,最高效的方式则是使用环信的 ChatroomUIKit 。 文档地址…...
nginx实现多个域名和集群
要通过Nginx实现多个域名和集群,你需要配置Nginx作为反向代理服务器,将来自不同域名的请求转发到集群中的相应后端服务器。下面是一个基本的配置示例,你可以根据自己的需求进行修改和扩展。 首先,确保你已经安装了Nginxÿ…...
C. Left and Right Houses
Problem - C - Codeforces 题目分析 <1>0:想被分割至左边; 1:想被分割至右边 <2>使得左右两侧均有一半及其以上的人满意(我*******) <3>答案若有多个,取最接近中间位置的答案 <4…...
缓存与内存:加速你的Python应用
在现代计算中,缓存和内存是提高程序性能的关键组件。在这篇文章中,我们将深入探讨这两个概念,了解它们是如何工作的,以及如何在Python中有效地使用它们来优化你的程序。 缓存与内存:加速你的Python应用 缓存和内存&…...

Go语言之函数、方法、接口
一、函数 函数的基本语法: func 函数名(形参列表)(返回值列表) {执行语句...return 返回值列表 } 1.形参列表:表示函数的输入 2.函数中的语句:表示为了实现某一功能的代码块 3.函数可以有返回…...

【Week Y2】使用自己的数据集训练YOLO-v5s
Y2-使用自己的数据集训练YOLO-v5s 零、遇到的问题汇总(1)遇到git的import error(2)Error:Dataset not found(3)Error:删除中文后,训练图片路径不存在 一、.xml文件里保存…...

蓝桥杯--基础(哈夫曼)
import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Scanner;public class BASIC28 {//哈夫曼书public static void main(String[] args) {Scanner Scannernew Scanner(System.in);int nScanner.nextInt();List<Integer&…...
【Redis内存数据库】NoSQL的特点和应用场景
前言 Redis作为当今最流行的内存数据库,已经成为服务端加速的必备工具之一。 NoSQL数据库采用了非关系型的数据存储模型,能够更好地处理海量数据和高并发访问。 内存数据库具有更快的读写速度和响应时间,因为内存访问速度比磁盘访问速度快…...

JavaScript基础知识2
求数组的最大值案例 let arr[2,6,1,7,400,55,88,100]let maxarr[0]let minarr[0]for(let i1;i<arr.length;i){max<arr[i]?maxarr[i]:maxmin>arr[i]?minarr[i]:min}console.log(最大值是:${max})console.log(最小值是:${min}) 操作数组 修改…...

Linux之线程同步
目录 一、问题引入 二、实现线程同步的方案——条件变量 1、常用接口: 2、使用示例 一、问题引入 我们再次看看上次讲到的多线程抢票的代码:这次我们让一个线程抢完票之后不去做任何事。 #include <iostream> #include <unistd.h> #inc…...
03 龙芯平台openstack部署搭建-keystone部署
#!/bin/bash #创建keystone数据库并授权,可通过mysql -ukeystone -ploongson验证授权登录 mysql -uroot -e “set password for rootlocalhost password(‘loongson’);” mysql -uroot -ploongson -e ‘CREATE DATABASE keystone;’ #本地登录 mysql -uroot -ploo…...
定义了服务器的端口号和Servlet的上下文路径
server: port: 1224 servlet: context-path: /applet 这个配置定义了服务器的端口号和Servlet的上下文路径。 下面是配置的解释: server.port: 1224:这表示服务器应该监听在1224端口上。server.servlet.context-path: /applet:这表…...

AI论文速读 | UniST:提示赋能通用模型用于城市时空预测
本文是时空领域的统一模型——UniST,无独有偶,时序有个统一模型新工作——UniTS,感兴趣的读者也可以阅读今天发布的另外一条。 论文标题:UniST: A Prompt-Empowered Universal Model for Urban Spatio-Temporal Prediction 作者&…...

rabbitmq-spring-boot-start配置使用手册
rabbitmq-spring-boot-start配置使用手册 文章目录 1.yaml配置如下2.引入pom依赖如下2.1 引入项目resources下libs中的jar包依赖如下2.2引入maven私服依赖如下 3.启动类配置如下4.项目中测试发送消息如下5.项目中消费消息代码示例6.mq管理后台交换机队列创建及路由绑定关系如下…...

操作系统知识-操作系统作用+进程管理-嵌入式系统设计师备考笔记
0、前言 本专栏为个人备考软考嵌入式系统设计师的复习笔记,未经本人许可,请勿转载,如发现本笔记内容的错误还望各位不吝赐教(笔记内容可能有误怕产生错误引导)。 本章的主要内容见下图: 1、操作系统的作用…...

Go语言中的锁与管道的运用
目录 1.前言 2.锁解决方案 3.管道解决方案 4.总结 1.前言 在写H5小游戏的时候,由于需要对多个WebSocket连接进行增、删、查的管理和对已经建立连接的WebSocket通过服务端进行游戏数据交换的需求。于是定义了一个全局的map集合进行连接的管理,让所有…...

前端 - 基础 表单标签 -- 表单元素( input - type属性) 文本框和密码框
表单元素 : 在表单域中可以定义各种表单元素,这些表单元素就是允许用户在表单中输入或选择 的内容控件。 表单元素的外观也各不一样,有小圆圈,有正方形,也有方框,乱七八糟的,各种各样…...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...

关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...