1. 概述
查找所有以星期日开头的年份看似简单,但在实际业务场景中却相当实用。特定日期和日历结构常影响运营、事件或调度安排,例如: ✅ 节假日/宗教活动调度 ✅ 薪资和工作计划 ✅ 财务周期计算
本文将介绍三种在指定年份范围内查找星期日开头年份的方法:
- 使用传统
Date
和Calendar
类 - 使用 Java 8 的
java.time
API - 基于
Spliterator<LocalDate>
的优化方案
2. 传统解决方案
先踩个坑:使用过时的 Calendar
类实现。核心思路是检查每年1月1日是否为星期日:
public class FindSundayStartYearsLegacy {
public static List<Integer> getYearsStartingOnSunday(int startYear, int endYear) {
List<Integer> years = new ArrayList<>();
for (int year = startYear; year <= endYear; year++) {
Calendar calendar = new GregorianCalendar(year, Calendar.JANUARY, 1);
if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
years.add(year);
}
}
return years;
}
}
⚠️ 关键点说明:
- 为每年创建
GregorianCalendar
实例并设置日期为1月1日 - 通过
calendar.get(Calendar.DAY_OF_WEEK)
获取星期值 - 与
Calendar.SUNDAY
常量比较
测试用例验证(2000-2025年范围):
@Test
public void givenYearRange_whenCheckingStartDayLegacy_thenReturnYearsStartingOnSunday() {
List<Integer> expected = List.of(2006, 2012, 2017, 2023);
List<Integer> result = FindSundayStartYearsLegacy.getYearsStartingOnSunday(2000, 2025);
assertEquals(expected, result);
}
3. 新API解决方案
简单粗暴:用 Java 8 的 java.time
包。核心改进是使用不可变的 LocalDate
:
public class FindSundayStartYearsTimeApi {
public static List<Integer> getYearsStartingOnSunday(int startYear, int endYear) {
List<Integer> years = new ArrayList<>();
for (int year = startYear; year <= endYear; year++) {
LocalDate date = LocalDate.of(year, 1, 1);
if (date.getDayOfWeek() == DayOfWeek.SUNDAY) {
years.add(year);
}
}
return years;
}
}
✅ 优势对比传统方案:
| 维度 | Calendar
方案 | java.time
方案 |
|------------|----------------|------------------|
| 不可变性 | ❌ 可变对象 | ✅ 不可变 |
| API设计 | ❌ 臃肿复杂 | ✅ 清晰简洁 |
| 线程安全 | ❌ 非线程安全 | ✅ 线程安全 |
| 废弃方法 | ❌ 大量废弃 | ✅ 全新设计 |
测试验证(预期结果一致):
@Test
public void givenYearRange_whenCheckingStartDayTimeApi_thenReturnYearsStartingOnSunday() {
List<Integer> expected = List.of(2006, 2012, 2017, 2023);
List<Integer> result = FindSundayStartYearsTimeApi.getYearsStartingOnSunday(2000, 2025);
assertEquals(expected, result);
}
4. 流式处理增强
性能优化:结合 Spliterator
和 Stream API。特别适合处理大范围年份:
public class FindSundayStartYearsSpliterator {
public static List<Integer> getYearsStartingOnSunday(int startYear, int endYear) {
Spliterator<LocalDate> spliterator = Spliterators.spliterator(
new Iterator<LocalDate>() {
int currentYear = startYear;
@Override
public boolean hasNext() {
return currentYear <= endYear;
}
@Override
public LocalDate next() {
return LocalDate.of(currentYear++, 1, 1);
}
},
endYear - startYear + 1,
Spliterator.ORDERED | Spliterator.SIZED
);
return StreamSupport.stream(spliterator, false)
.filter(date -> date.getDayOfWeek() == DayOfWeek.SUNDAY)
.map(LocalDate::getYear)
.collect(Collectors.toList());
}
}
⚡ 核心优化点:
- **自定义
Spliterator
**:高效遍历年份范围 - 并行处理:
StreamSupport.stream(spliterator, true)
可启用并行 - 惰性求值:Stream 链式操作提升内存效率
- 函数式风格:更符合现代 Java 开发范式
测试验证:
@Test
public void givenYearRange_whenCheckingStartDaySpliterator_thenReturnYearsStartingOnSunday() {
List<Integer> expected = List.of(2006, 2012, 2017, 2023);
List<Integer> result = FindSundayStartYearsSpliterator.getYearsStartingOnSunday(2000, 2025);
assertEquals(expected, result);
}
5. 总结
三种方案对比:
方案 | 适用场景 | 推荐指数 |
---|---|---|
Calendar 方案 |
维护旧代码 | ⭐ |
java.time 方案 |
新项目开发 | ⭐⭐⭐⭐⭐ |
Spliterator 方案 |
大数据量/并行处理需求 | ⭐⭐⭐⭐ |
最佳实践建议:
- ✅ 优先使用
java.time
API(类型安全/不可变/清晰) - ⚠️ 处理超大年份范围时考虑
Spliterator
方案 - ❌ 避免在新代码中使用
Date
/Calendar
实际项目中,
java.time
方案已能满足99%的需求。只有当处理百年级别年份范围时,才需要考虑Spliterator
的并行优化能力。