如何改进此算法以获取 Java 8 或 11 中每个月的工作日数

问题描述 投票:0回答:2

我想知道如何改进这个非常简单的代码。

代码极其冗长,两个 for 循环绝对不实用或简洁。

我正在考虑使用

Stream.iterate().limit(LAST_DAY_MONTH)
,但是当我们需要返回值时它并不能解决。

public class WeekDaysPerMonth {

    public Map getNumberWeekDaysPerMonth(int year) {
        Map<Integer, Integer> weekDays = new Hashtable<>();
        for (int monthCount = 1; monthCount <= 12; monthCount++) {
            int daysCount = 0;
            final int LAST_DAY_MONTH = getLastDayMonth(monthCount, year);
            for (int day = 1; day <= LAST_DAY_MONTH; day++) {
                final int weekDay = LocalDate.of(year, monthCount, day).getDayOfWeek().getValue();
                daysCount = daysCount + (
                            (weekDay != DayOfWeek.SATURDAY.getValue()
                                && weekDay != DayOfWeek.SUNDAY.getValue())
                        ? 1 : 0);
            }
            weekDays.put(monthCount, daysCount);
        }
        return weekDays;
    }

    private int getLastDayMonth(int month, int year) {
        final boolean yearBiSexto = (year % 4 ) == 0;
        Map<Integer, Integer> lastDayMontMap = new HashMap<Integer, Integer>() {{
           put(1, 31);
           put(2, yearBiSexto ? 29 : 28);
           put(3, 31);
           put(4, 30);
           put(5, 31);
           put(6, 30);
           put(7, 31);
           put(8, 31);
           put(9, 30);
           put(10, 31);
           put(11, 30);
           put(12, 31);
        }};

        return lastDayMontMap.get(month);
    }

这是我们提供2022年时的结果。

mes : 1 - mes : 21
mes : 2 - mes : 20
mes : 3 - mes : 23
mes : 4 - mes : 21
mes : 5 - mes : 22
mes : 6 - mes : 22
mes : 7 - mes : 21
mes : 8 - mes : 23
mes : 9 - mes : 22
mes : 10 - mes : 21
mes : 11 - mes : 22
mes : 12 - mes : 22

拜托,任何改进都会很棒!

java data-structures java-stream java-11 java-time
2个回答
2
投票

基于实现java.timeJava 8。

  • 不包含硬编码值
  • 流风格
import java.time.DayOfWeek;
import java.time.Month;
import java.time.YearMonth;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class Main {
    public static void main(String args[]){
        Map<String, Long> map = getNumberWeekDaysPerMonth(2022);

        for (Map.Entry<String, Long> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue().toString());
        }

    }

    public static Map<String, Long> getNumberWeekDaysPerMonth(int year) {
        Map<String, Long> weekDays = new LinkedHashMap<>();
        EnumSet<DayOfWeek> weekends = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );

        Stream.of(Month.values()).forEach(month -> {
                    YearMonth yearMonth = YearMonth.of(year, month);
                    long countWeekDays = IntStream.rangeClosed(1, yearMonth.lengthOfMonth()).filter(day ->
                            !weekends.contains(yearMonth.atDay(day).getDayOfWeek())
                    ).count();
                    weekDays.put(month.toString(), countWeekDays);
                }
        );

        return weekDays;
    }
}

输出:

JANUARY:21
FEBRUARY:20
MARCH:23
APRIL:21
MAY:22
JUNE:22
JULY:21
AUGUST:23
SEPTEMBER:22
OCTOBER:21
NOVEMBER:22
DECEMBER:22


YearMonth - 是一个不可变的日期时间对象,表示年和月的组合
Month - 是代表一年中 12 个月的枚举
LocalDate.getDayOfWeek() - 获取日期的星期几。

编辑:收集到一个流,但前一个变体更容易理解

    public static Map<String, Long> getNumberWeekDaysPerMonth(int year) {
        EnumSet<DayOfWeek> weekends = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
        return Stream.of(Month.values()).collect(Collectors.toMap(
                Enum::toString,
                month ->  IntStream.rangeClosed(1, YearMonth.of(year, month).lengthOfMonth()).
                          filter(day -> !weekends.contains(LocalDate.of(year, month, day).getDayOfWeek())).
                          count(),
                (e1, e2) -> e1,
                LinkedHashMap::new));
    }

0
投票

这是一个基于流的解决方案,它使用

Map
 将值收集到 
Collectors.toMap
中,并利用 Java 时间 API(您已经在使用)来确定有关所涉及的日期和月份的详细信息:

public Map<Integer, Integer> getNumberWeekDaysPerMonth(int year) {
    EnumSet<DayOfWeek> weekDays = EnumSet.range(DayOfWeek.MONDAY, DayOfWeek.FRIDAY);

    return Arrays.stream(Month.values())
            .map(month -> YearMonth.of(year, month))
            .collect(Collectors.toMap(
                    YearMonth::getMonthValue,
                    yearMonth -> (int) IntStream.rangeClosed(1, yearMonth.lengthOfMonth())
                            .mapToObj(d -> yearMonth.atDay(d).getDayOfWeek())
                            .filter(weekDays::contains)
                            .count()));
}

valueMapper
的第二个参数
Collectors.toMap
获取给定月份的天数。 它采用给定的
YearMonth
并流式传输该月从 1 到该月最后一天的所有天数。 它将这些天映射到星期几,使用
YearMonth.atDay
将日期转换为
LocalDate
,并使用
LocalDate.getDayOfWeek
获取该特定日期的
DayOfWeek
。 然后,它将流过滤为仅工作日,并使用
Stream.count
获取剩余项目的计数。

© www.soinside.com 2019 - 2024. All rights reserved.