蓝桥云课-13. 定时任务

目录

题目链接:

题目:

解题思路:

代码:

问题:

总结:


题目链接:

13.定时任务 - 蓝桥云课

题目:

解题思路:

就是一些Java的Api的使用,刚开始使用nextLine进行输入,然后使用split(" ")分割并封装到字符串数组中,这样便于后续的操作,

然后进行操作,使用contais判断是否包含某个操作进而进行操作

代码:

java 复制代码
 /*out
*Stack Integer ArrayList String StringBuffer peek
*Collections imports LinkedList offer return
*empty polls offerLast pollFirst isEmpty
*List Deque append length HashMap
*return remove boolean continue charAt
*toString static System println nextInt
*Scanner  System  toCharArray
*/
import java.util.*;
public class Main {
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
        int t=1;
        while(t--!=0){
          slove();
        }
        sc.close();
    }
    static int[] unit={59,59,23,31,12};
    static int[] mon={0,31,28,31,30,31,30,31,31,30,31,30,31};
    public static void slove(){
      String[] s=sc.nextLine().split(" ");
      //这个二维动态数组的方式要知道
      List<Integer>[] a=new ArrayList[5];
      for(int i=0;i<s.length;i++){
        a[i] = new ArrayList<>();
        if(s[i].contains("*")){
          for(int j=i==4||i==3?1:0;j<=unit[i];j++)
            a[i].add(j);
        }else if(s[i].contains(",")){
          String[] fu=s[i].split(",");
          for(int j=0;j<fu.length;j++){
            a[i].add(Integer.parseInt(fu[j]));
          }
        }else if(s[i].contains("-")){
          String[] fu=s[i].split("-");
          for(int j=Integer.parseInt(fu[0]);j<=Integer.parseInt(fu[1]);j++){
            a[i].add(j);
          }
        }else{
          a[i].add(Integer.parseInt(s[i]));
        }
      }
      int ans=0;
      for(int i=0;i<a[4].size();i++){
        for(int j=0;j<a[3].size()&&a[3].get(j)<=mon[a[4].get(i)];j++){
          ans += a[0].size() * a[1].size() * a[2].size(); 
        }
      }
      System.out.println(ans);
    }
}

问题:

总结:

要理解这段代码,我们需要结合题目背景(SimpleCron 表达式解析)和代码逻辑(时间域解析、执行次数计算),从项目结构、核心变量、流程逻辑、细节实现四个维度展开分析,同时解释代码中涉及的 Java 语法和算法思想。

一、题目背景回顾:SimpleCron 表达式的规则

题目中定义的SimpleCron表达式是一个由4 个空格分隔的 5 个域组成的字符串,格式为X X X X X,每个域的含义和取值范围如下:

域的顺序 含义 取值范围 支持的特殊字符

第 1 个域 秒 0-59 *(所有值)、,(枚举)、-(范围)

第 2 个域 分钟 0-59 同上

第 3 个域 小时 0-23 同上

第 4 个域 日期 1-31 同上

第 5 个域 月份 1-12 同上

任务是计算 2023 年中该表达式对应的定时任务的总执行次数(2023 年是平年,2 月有 28 天)。

二、代码整体结构与核心变量说明

这段代码是一个标准的 Java 控制台程序,核心结构分为:

静态变量:存储全局通用数据;

main 方法:程序入口,控制执行流程;

slove 方法:核心逻辑(解析表达式、计算执行次数)。

  1. 静态变量解析

java

运行

static Scanner sc = new Scanner(System.in);

static int\[\] unit={59,59,23,31,12};

static int\[\] mon={0,31,28,31,30,31,30,31,31,30,31,30,31};

sc:Scanner对象,用于读取控制台输入的 SimpleCron 字符串;

unit数组:存储 5 个域的最大取值,对应关系为:

unit0 = 59 → 秒的最大值(0-59);

unit1 = 59 → 分钟的最大值(0-59);

unit2 = 23 → 小时的最大值(0-23);

unit3 = 31 → 日期的最大值(1-31);

unit4 = 12 → 月份的最大值(1-12);

mon数组:存储 2023 年每个月的天数,mon月份 = 该月天数(例如mon2=28表示 2 月有 28 天),注意mon0是占位符(因为月份从 1 开始)。

  1. main 方法:程序入口

java

运行

public static void main(String\[\] args) {

int t=1;

while(t--!=0){

slove();

}

sc.close();

}

这里用while(t--!=0)循环执行slove()方法,实际t=1,所以只执行 1 次;

最后调用sc.close()关闭 Scanner,避免资源泄漏。

三、核心逻辑:slove 方法的详细解析

slove方法是代码的核心,分为两步:

解析 SimpleCron 表达式:将 5 个域的取值转换为List<Integer>集合;

计算 2023 年的总执行次数:遍历合法的月份和日期,结合时分秒的组合数统计次数。

步骤 1:解析 SimpleCron 表达式(将字符串转换为 List 集合)

首先读取输入的表达式并分割为 5 个域:

java

运行

String\[\] s=sc.nextLine().split(" ");

List<Integer>\[\] a=new ArrayList5;

s = sc.nextLine().split(" "):将输入的字符串按空格分割为 5 个元素的数组(对应 5 个域);

List<Integer>\[\] a = new ArrayList5:创建一个数组,数组的每个元素是List<Integer>类型(存储对应域的所有合法取值)。这里是 "数组套集合" 的结构,因为每个域的合法取值数量不固定(比如*对应所有值,,对应枚举值)。

接下来遍历每个域,解析其合法取值并存储到ai中:

java

运行

for(int i=0;i<s.length;i++){

ai = new ArrayList<>(); // 初始化当前域的List

if(si.contains("*")){

// 处理*:表示该域的所有可能值

for(int j=i==4||i==3?1:0;j<=uniti;j++)

ai.add(j);

}else if(si.contains(",")){

// 处理,:枚举多个值

String\[\] fu=si.split(",");

for(int j=0;j<fu.length;j++){

ai.add(Integer.parseInt(fuj));

}

}else if(si.contains("-")){

// 处理-:范围值

String\[\] fu=si.split("-");

for(int j=Integer.parseInt(fu0);j<=Integer.parseInt(fu1);j++){

ai.add(j);

}

}else{

// 处理单个值

ai.add(Integer.parseInt(si));

}

}

我们逐个分析4 种情况的解析逻辑:

情况 1:处理特殊字符*(所有可能值)

java

运行

if(si.contains("*")){

for(int j=i==4||i==3?1:0;j<=uniti;j++)

ai.add(j);

}

i==4||i==3?1:0:这是三元运算符,用于确定循环的起始值:

当i=3(日期域)或i=4(月份域)时,起始值为1(因为日期的取值是 1-31,月份是 1-12);

当i=0(秒)、i=1(分钟)、i=2(小时)时,起始值为0(对应取值范围 0-59、0-23);

循环范围:从起始值到uniti(该域的最大值),将所有值加入ai

例如:

若月份域是*,则i=4,循环从 1 到 12,a4中存储1,2,...,12

若秒域是*,则i=0,循环从 0 到 59,a0中存储0,1,...,59

情况 2:处理特殊字符,(枚举值)

java

运行

else if(si.contains(",")){

String\[\] fu=si.split(",");

for(int j=0;j<fu.length;j++){

ai.add(Integer.parseInt(fuj));

}

}

split(","):将当前域的字符串按逗号分割为多个子串(例如"2,3,20"分割为"2","3","20");

Integer.parseInt(fuj):将子串转换为整数,加入ai

例如:若秒域是"2,3,20",则a0中存储2,3,20

情况 3:处理特殊字符-(范围值)

java

运行

else if(si.contains("-")){

String\[\] fu=si.split("-");

for(int j=Integer.parseInt(fu0);j<=Integer.parseInt(fu1);j++){

ai.add(j);

}

}

split("-"):将当前域的字符串按减号分割为两个子串(例如"1-5"分割为"1","5");

循环范围:从分割后的第一个值到第二个值,将中间的所有整数加入ai

例如:若小时域是"1-31",则a3中存储1,2,...,31

情况 4:处理单个值(无特殊字符)

java

运行

else{

ai.add(Integer.parseInt(si));

}

直接将当前域的字符串转换为整数,加入ai

例如:若分钟域是"2",则a1中存储2

步骤 2:计算 2023 年的总执行次数

解析完成后,a0(秒)、a1(分钟)、a2(小时)、a3(日期)、a4(月份)分别存储了对应域的所有合法取值。

接下来计算总执行次数:

java

运行

int ans=0;

for(int i=0;i<a4.size();i++){

// 当前月份:a4.get(i)

int currentMonth = a4.get(i);

for(int j=0;j<a3.size()&&a3.get(j)<=moncurrentMonth;j++){

// 累加:秒的数量 × 分钟的数量 × 小时的数量

ans += a0.size() * a1.size() * a2.size();

}

}

System.out.println(ans);

我们分层次理解这段逻辑:

第一层循环:遍历所有合法月份

java

运行

for(int i=0;i<a4.size();i++){

int currentMonth = a4.get(i);

// ...

}

a4存储的是月份域的所有合法取值(例如1,3,5表示 1 月、3 月、5 月);

currentMonth是当前遍历到的月份(例如1对应 1 月)。

第二层循环:遍历合法日期(需满足 "日期≤当月天数")

java

运行

for(int j=0;j<a3.size()&&a3.get(j)<=moncurrentMonth;j++){

// ...

}

a3存储的是日期域的所有合法取值(例如1,3,15,31);

a3.get(j)<=moncurrentMonth:这是关键的合法性判断 ------ 因为不同月份的天数不同(例如 2 月只有 28 天),所以需要过滤掉 "日期超过当月天数" 的情况(例如 2 月的 31 日是无效日期);

只有当日期≤moncurrentMonth(当前月份的天数)时,该日期才是合法的。

累加执行次数:时分秒的组合数

java

运行

ans += a0.size() * a1.size() * a2.size();

组合数原理:对于每个 "合法的月份 + 日期",任务的执行次数等于秒的合法取值数量 × 分钟的合法取值数量 × 小时的合法取值数量(因为时分秒的取值是独立的)。

例如:

若秒域的合法取值有 3 个(a0.size()=3),分钟域有 1 个(a1.size()=1),小时域有 2 个(a2.size()=2),则每个 "月 + 日" 对应的执行次数是3×1×2=6次。

四、结合题目样例验证代码逻辑

题目中给出的样例:

SimpleCron 表达式:4 21,3,15 1-31 *(注意题目中的格式可能有笔误,实际应为 5 个域,假设正确表达式是4 2 1,3,15 1-31 *);

执行次数:2023 年共执行 1095 次。

我们用代码逻辑验证:

解析各域:

秒域:"4" → a0 = 4 → 大小为 1;

分钟域:"2" → a1 = 2 → 大小为 1;

小时域:"1,3,15" → a2 = 1,3,15 → 大小为 3;

日期域:"1-31" → a3 = 1,2,...,31 → 大小为 31;

月份域:"*" → a4 = 1,2,...,12 → 大小为 12;

计算执行次数:

对于每个月份,合法日期数为moncurrentMonth(例如 1 月有 31 天,2 月有 28 天);

每个 "月 + 日" 对应的执行次数是1×1×3=3次;

总次数 = 所有月份的合法日期数之和 × 3;

2023 年各月天数之和是31+28+31+30+31+30+31+31+30+31+30+31=365;

总次数 = 365 × 3 = 1095次(与题目样例一致)。

五、代码的优缺点与优化点

优点:

逻辑清晰:将 "解析表达式" 和 "计算次数" 分离,代码分层明确;

效率较高:通过集合存储合法取值,避免重复计算;

兼容性强:覆盖了题目要求的所有特殊字符(*、,、-)和单个值的情况。

缺点与优化点:

泛型数组的警告:List<Integer>\[\] a = new ArrayList5会产生 "未检查的转换" 警告,因为 Java 不允许直接创建泛型数组,建议改用List<List<Integer>> a = new ArrayList<>()(更符合泛型规范);

日期合法性判断的边界问题:a3.get(j)<=moncurrentMonth中,若a3包含超过 31 的值(但题目说明输入是合法的,所以风险较低);

代码命名规范:slove是拼写错误(应为solve),fu、a等变量名不直观(建议改为fields、domainValues)。

六、总结

这段代码是一个针对 SimpleCron 表达式的解析与执行次数计算工具,核心思想是:

将表达式的每个域转换为合法取值的集合(通过解析*、,、-等特殊字符);

利用组合数学原理,将 "月 - 日" 的合法组合数与 "时 - 分 - 秒" 的组合数相乘,得到总执行次数;

结合 2023 年的实际月份天数,过滤掉无效日期,确保结果的准确性。

代码通过 "数组 + 集合" 的结构灵活存储动态取值,通过双层循环遍历合法的时间维度,最终高效计算出定时任务的总执行次数,完全符合题目的要求。

相关推荐
wuminyu14 小时前
Java世界中StringTable源码剖析
java·linux·c语言·jvm·c++
hairenwangmiao14 小时前
B4041 [GESP202409 四级] 区间排序
算法·排序
一个做软件开发的牛马14 小时前
Spring Boot 自动配置原理揭秘:从 @SpringBootApplication 到手写自定义 Starter
java·后端
一起吃元宵14 小时前
百度网盘下载不限速的办法_百度网盘不限速
开发语言·百度网盘·下载不限速·不限速·百度网盘不限速
人道领域14 小时前
【LeetCode刷题日记】47.全排列Ⅱ
java·开发语言·算法·leetcode
漂流瓶jz14 小时前
UVA-1606 两亲性分子 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·向量·aoapc·算法竞赛入门经典·atan2·浮点
Navigator_Z14 小时前
LeetCode //C - 1095. Find in Mountain Array
c语言·算法·leetcode
ch3nyuyu14 小时前
socket套接字
开发语言·php
源图客15 小时前
境外电商 - 龙虾智能体-综合选品推荐报告
开发语言·javascript·ecmascript
不会就选b15 小时前
算法日常・每日刷题--<二分查找>1
算法