蓝桥云课-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 个域的最大取值,对应关系为:

unit[0] = 59 → 秒的最大值(0-59);

unit[1] = 59 → 分钟的最大值(0-59);

unit[2] = 23 → 小时的最大值(0-23);

unit[3] = 31 → 日期的最大值(1-31);

unit[4] = 12 → 月份的最大值(1-12);

mon数组:存储 2023 年每个月的天数,mon[月份] = 该月天数(例如mon[2]=28表示 2 月有 28 天),注意mon[0]是占位符(因为月份从 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 ArrayList[5];

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

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

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

java

运行

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

a[i] = new ArrayList<>(); // 初始化当前域的List

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]));

}

}

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

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

java

运行

if(s[i].contains("*")){

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

a[i].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);

循环范围:从起始值到unit[i](该域的最大值),将所有值加入a[i]。

例如:

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

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

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

java

运行

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]));

}

}

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

Integer.parseInt(fu[j]):将子串转换为整数,加入a[i]。

例如:若秒域是"2,3,20",则a[0]中存储[2,3,20]。

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

java

运行

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);

}

}

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

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

例如:若小时域是"1-31",则a[3]中存储[1,2,...,31]。

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

java

运行

else{

a[i].add(Integer.parseInt(s[i]));

}

直接将当前域的字符串转换为整数,加入a[i]。

例如:若分钟域是"2",则a[1]中存储[2]。

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

解析完成后,a[0](秒)、a[1](分钟)、a[2](小时)、a[3](日期)、a[4](月份)分别存储了对应域的所有合法取值。

接下来计算总执行次数:

java

运行

int ans=0;

for(int i=0;i<a[4].size();i++){

// 当前月份:a[4].get(i)

int currentMonth = a[4].get(i);

for(int j=0;j<a[3].size()&&a[3].get(j)<=mon[currentMonth];j++){

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

ans += a[0].size() * a[1].size() * a[2].size();

}

}

System.out.println(ans);

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

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

java

运行

for(int i=0;i<a[4].size();i++){

int currentMonth = a[4].get(i);

// ...

}

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

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

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

java

运行

for(int j=0;j<a[3].size()&&a[3].get(j)<=mon[currentMonth];j++){

// ...

}

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

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

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

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

java

运行

ans += a[0].size() * a[1].size() * a[2].size();

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

例如:

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

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

题目中给出的样例:

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

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

我们用代码逻辑验证:

解析各域:

秒域:"4" → a[0] = [4] → 大小为 1;

分钟域:"2" → a[1] = [2] → 大小为 1;

小时域:"1,3,15" → a[2] = [1,3,15] → 大小为 3;

日期域:"1-31" → a[3] = [1,2,...,31] → 大小为 31;

月份域:"*" → a[4] = [1,2,...,12] → 大小为 12;

计算执行次数:

对于每个月份,合法日期数为mon[currentMonth](例如 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 ArrayList[5]会产生 "未检查的转换" 警告,因为 Java 不允许直接创建泛型数组,建议改用List<List<Integer>> a = new ArrayList<>()(更符合泛型规范);

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

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

六、总结

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

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

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

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

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

相关推荐
Unstoppable221 小时前
八股训练营第 37 天 | Java 内存区域有哪些部分?介绍一下什么是强引用、软引用、弱引用、虚引用?有哪些垃圾回收算法?有哪些垃圾回收器?
java·jvm·八股
JIngJaneIL1 小时前
基于Java民宿管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot
脸大是真的好~1 小时前
尚硅谷-mysql专项训练-InnoDB数据存储结构-索引的创建与设计
java
竹林幽深1 小时前
集群环境下SSE的解决方案-没试记录一下
java·spring boot·后端
m0_726965981 小时前
RAG小实战
开发语言·python
元素之窗1 小时前
MATLAB 的输入与输出:一篇速查博客
开发语言·matlab·php
一个不知名程序员www1 小时前
算法学习入门---list与算法竞赛中的链表题(C++)
c++·算法
沉默GAVIN1 小时前
How Apps Communicate with ActivityManagerService
java
CoderYanger1 小时前
动态规划算法-路径问题:9.最小路径和
开发语言·算法·leetcode·动态规划·1024程序员节