ТЛ; др
Используя современные функции и классы языка Java, определите свои собственные перечисления для представления сезонов и групп.
Schedule.daysForGroupOnDate(
Group.D ,
LocalDate.now()
)
Этот метод возвращает Set
из DayOfWeek
перечисляемых объектов (не просто текста!), Таких как DayOfWeek.TUESDAY
& DayOfWeek.THURSDAY
.
Современная Java
Кто-нибудь может предложить действительно крутой способ сделать это?
Да.
Современная Java имеет встроенные классы, коллекции и перечисления, чтобы помочь вам с этой проблемой.
Встроенный в Java фреймворк java.time предлагает перечисление Month
и перечисление DayOfWeek
.
EnumSet
и EnumMap
предоставляют реализации Set
и Map
, оптимизированные для использования с перечислениями для быстрого выполнения в очень маленькой памяти.
Вы можете определить свои перечисления для представления своего сезона и своих групп (A, B и т. Д.). Функция enum в Java гораздо более полезна и мощна, чем в других языках. Если вы не знакомы, см. Oracle Tutorial .
Простой синтаксис определения ваших собственных перечислений фактически обеспечивает большую часть функциональности, необходимой для решения этого Вопроса, устраняя некоторые сложные кодирования. Новый синтаксис литералов для наборов и карт в методах фабрики в Java 9 ( JEP 269 ) делает код еще проще.
Вот полное рабочее приложение. Обратите внимание, как мало кода для алгоритмов. Определение собственных пользовательских перечислений делает большую часть тяжелой работы.
Одно предупреждение с этим кодом приложения: оно предполагает, что ничего не изменится в определениях вашего бизнеса, или, по крайней мере, если есть изменение, которое вас волнует только в текущих правилах, «последнее - самое большое». Если ваши правила со временем меняются и вам необходимо представлять все прошлые, настоящие и будущие версии, я бы создал совсем другое приложение, возможно, с базой данных для хранения правил. Но это приложение здесь решает вопрос, как задано.
Season
Представьте свой сезон как перечисление Season
. Каждый объект сезона содержит объекты перечисления List
из Month
, которые определяют продолжительность этого конкретного сезона. Новый синтаксис List.of
, добавленный в Java 9, определяет неизменный список в синтаксисе литералов с помощью статических фабричных методов.
package com.basilbourque.watering;
import java.time.LocalDate;
import java.time.Month;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
public enum Season
{
SPRING( List.of( Month.MARCH , Month.APRIL ) ),
SUMMER( List.of( Month.MAY , Month.JUNE, Month.JULY , Month.AUGUST ) ),
FALL( List.of( Month.SEPTEMBER , Month.OCTOBER ) ),
WINTER( List.of( Month.NOVEMBER , Month.DECEMBER , Month.JANUARY , Month.FEBRUARY ) );
private List< Month > months;
// Constructor
Season ( List < Month > monthsArg )
{
this.months = monthsArg;
}
public List < Month > getMonths ( )
{
return this.months;
}
// For any given month, determine the season.
static public Season ofLocalMonth ( Month monthArg )
{
Season s = null;
for ( Season season : EnumSet.allOf( Season.class ) )
{
if ( season.getMonths().contains( monthArg ) )
{
s = season;
break; // Bail out of this FOR loop.
}
}
return s;
}
// For any given date, determine the season.
static public Season ofLocalDate ( LocalDate localDateArg )
{
Month month = localDateArg.getMonth();
Season s = Season.ofLocalMonth( month );
return s;
}
// Run `main` for demo/testing.
public static void main ( String[] args )
{
// Dump all these enum objects to console.
for ( Season season : EnumSet.allOf( Season.class ) )
{
System.out.println( "Season: " + season.toString() + " = " + season.getMonths() );
}
}
}
Group
Представляет каждую группу газонов / дворов клиентов (A, B, C, D, E) как перечисление с именем Group
. Каждый из этих объектов перечисления содержит Map
, сопоставляя объект перечисления Season
с Set
из DayOfWeek
объектов перечисления. Например, Group.A
в Season.SPRING
позволяет поливать в течение двух дней, DayOfWeek.TUESDAY
& DayOfWeek.THURSDAY
.
package com.basilbourque.watering;
import java.time.DayOfWeek;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
public enum Group
{
A(
Map.of(
Season.SPRING , EnumSet.of( DayOfWeek.TUESDAY , DayOfWeek.THURSDAY ) ,
Season.SUMMER , EnumSet.allOf( DayOfWeek.class ) ,
Season.FALL , EnumSet.of( DayOfWeek.TUESDAY , DayOfWeek.THURSDAY ) ,
Season.WINTER , EnumSet.of( DayOfWeek.TUESDAY )
)
),
B(
Map.of(
Season.SPRING , EnumSet.of( DayOfWeek.FRIDAY ) ,
Season.SUMMER , EnumSet.allOf( DayOfWeek.class ) ,
Season.FALL , EnumSet.of( DayOfWeek.TUESDAY , DayOfWeek.FRIDAY ) ,
Season.WINTER , EnumSet.of( DayOfWeek.FRIDAY )
)
),
C(
Map.of(
Season.SPRING , EnumSet.of( DayOfWeek.MONDAY ) ,
Season.SUMMER , EnumSet.allOf( DayOfWeek.class ) ,
Season.FALL , EnumSet.of( DayOfWeek.MONDAY , DayOfWeek.TUESDAY ) ,
Season.WINTER , EnumSet.of( DayOfWeek.MONDAY )
)
),
D(
Map.of(
Season.SPRING , EnumSet.of( DayOfWeek.WEDNESDAY , DayOfWeek.FRIDAY ) ,
Season.SUMMER , EnumSet.allOf( DayOfWeek.class ) ,
Season.FALL , EnumSet.of( DayOfWeek.FRIDAY ) ,
Season.WINTER , EnumSet.of( DayOfWeek.WEDNESDAY )
)
),
E(
Map.of(
Season.SPRING , EnumSet.of( DayOfWeek.TUESDAY ) ,
Season.SUMMER , EnumSet.allOf( DayOfWeek.class ) ,
Season.FALL , EnumSet.of( DayOfWeek.TUESDAY , DayOfWeek.WEDNESDAY ) ,
Season.WINTER , EnumSet.of( DayOfWeek.WEDNESDAY )
)
);
private Map < Season, Set < DayOfWeek > > map;
// Constructor
Group ( Map < Season, Set < DayOfWeek > > mapArg )
{
this.map = mapArg;
}
// Getter
private Map < Season, Set < DayOfWeek > > getMapOfSeasonToDaysOfWeek() {
return this.map ;
}
// Retrieve the DayOfWeek set for this particular Group.
public Set<DayOfWeek> daysForSeason (Season season ) {
Set<DayOfWeek> days = this.map.get( season ) ; // Retrieve the value (set of days) for this key (a season) for this particular grouping of lawns/yards.
return days;
}
// Run `main` for demo/testing.
public static void main ( String[] args )
{
// Dump all these enum objects to console.
for ( Group group : EnumSet.allOf( Group.class ) )
{
System.out.println( "Group: " + group.toString() + " = " + group.getMapOfSeasonToDaysOfWeek() );
}
}
}
Schedule
Соберите все вместе в этом Schedule
классе.
Этот класс использует два перечисленных выше перечисления для выполнения полезной работы. Единственный реализованный метод покажет вам, какие дни недели разрешены для определенной группы на определенную дату. Метод определяет, какой Season
применяется на эту дату.
Запустите здесь метод main
, чтобы выгрузить содержимое наших двух перечислений и сообщить о днях недели, в которые допускается полив в каждой группе для конкретной даты с жестким кодом.
package com.basilbourque.watering;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.IsoFields;
import java.util.EnumSet;
import java.util.Set;
public class Schedule
{
static private DateTimeFormatter isoWeekFormatter = DateTimeFormatter.ofPattern( "uuuu-'W'ww" ) ;
static public Set < DayOfWeek > daysForGroupOnDate ( Group group , LocalDate localDate )
{
Season season = Season.ofLocalDate( localDate );
Set < DayOfWeek > days = group.daysForSeason( season );
return days;
}
// Run `main` for demo/testing.
public static void main ( String[] args )
{
Season.main( null );
Group.main( null );
// Dump all these enum objects to console.
for ( Group group : EnumSet.allOf( Group.class ) )
{
LocalDate localDate = LocalDate.now( ZoneId.of( "Africa/Tunis" ) );
Set < DayOfWeek > days = Schedule.daysForGroupOnDate( group , localDate );
String week = localDate.format( Schedule.isoWeekFormatter ) ; // Standard ISO 8601 week, where week number one has the first Thursday of the calendar year, and week starts on Monday, so year is either 52 or 53 weeks long.
String message = "Group " + group + " – Watering days on " + localDate + " week # " + week + " is: " + days;
System.out.println( message );
}
}
}
Консоль
При запуске Schedule.main
мы видим, что это выводится на консоль.
Сезон: ВЕСНА = [МАРТ, АПРЕЛЬ]
Сезон: ЛЕТО = [МАЙ, ИЮНЬ, ИЮЛЬ, АВГУСТ]
Сезон: ОСЕНЬ = [СЕНТЯБРЬ, ОКТЯБРЬ]
Сезон: ЗИМА = [НОЯБРЬ, ДЕКАБРЬ, ЯНВАРЬ, ФЕВРАЛЬ]
Группа: A = {SPRING = [ВТОРНИК, ЧЕТВЕРГ], FALL = [ВТОРНИК, ЧЕТВЕРГ], ЛЕТО = = ПОНЕДЕЛЬНИК, ВТОРНИК, СРЕДА, СУББОТА, ПЯТНИЦА, СУББОТА, ВОСКРЕСЕНЬ], ВТОРНИК =] }26 *
Группа: B = {SPRING = [ПЯТНИЦА], FALL = [ВТОРНИК, ПЯТНИЦА], ЛЕТО = = понедельник, вторник, среда, четверг, пятница, суббота, воскресенье], зима = [пятница]}
Группа: C = {SPRING = [ПОНЕДЕЛЬНИК], FALL = [ПОНЕДЕЛЬНИК, ВТОРНИК], ЛЕТО = = ПОНЕДЕЛЬНИК, ВТОРНИК, СРЕДА, ЧЕТВЕРГ, ПЯТНИЦА, СУББОТА, Воскресенье], ЗИМА = [ПОНЕДЕЛЬНИК]}
Группа: D = {SPRING = [СРЕДА, ПЯТНИЦА], FALL = [ПЯТНИЦА], ЛЕТО = = понедельник, вторник, среда, четверг, четверг, пятница, суббота, воскресенье], зима = [среда]}
Группа: E = {SPRING = [ВТОРНИК], FALL = [ВТОРНИК, СРЕДА], ЛЕТО = = понедельник, вторник, среда, четверг, пятница, суббота, воскресенье], зима = [среда * 11]] *
Группа A - Дни полива на 2018-01-30 неделе # 2018-W05: [ВТОРНИК]
Группа B - дни полива на 2018-01-30 неделе # 2018-W05: [пятница]
Группа C - Дни полива на 2018-01-30 неделе # 2018-W05: [ПОНЕДЕЛЬНИК]
Группа D - дни полива на 2018-01-30 неделе # 2018-W05: [СРЕДА]
Группа E - Дни полива на 2018-01-30 неделе # 2018-W05: [СРЕДА]
ISO 8601 неделя
Может оказаться полезным узнать о стандарте ISO 8601 для определения недели . Стандарт придает определенное значение «неделе» и определяет текстовый формат для представления конкретной недели или определенного дня в пределах этой недели.
Для работы с такими неделями в Java рассмотрите возможность добавления библиотеки ThreeTen-Extra в свой проект, чтобы использовать класс YearWeek
.
LocalDate
Класс LocalDate
представляет значение только для даты без времени суток и без часового пояса.
Часовой пояс имеет решающее значение при определении даты. В любой момент времени дата меняется по всему земному шару в зависимости от зоны. Например, через несколько минут после полуночи в Париж Франция - это новый день, хотя еще «вчера» в Монреаль Квебек .
Если часовой пояс не указан, JVM неявно применяет свой текущий часовой пояс по умолчанию. Это значение по умолчанию может измениться в любой момент, поэтому ваши результаты могут отличаться. Лучше явно указать желаемый / ожидаемый часовой пояс в качестве аргумента.
Укажите собственное имя часового пояса в формате continent/region
, например America/Montreal
, Africa/Casablanca
или Pacific/Auckland
. Никогда не используйте 3-4-буквенное сокращение, например EST
или IST
, так как они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Если вы хотите использовать текущий часовой пояс JVM по умолчанию, запросите его и передайте в качестве аргумента. Если опущено, текущее значение по умолчанию JVM применяется неявно. Лучше быть явным.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Или укажите дату. Вы можете установить месяц по номеру, с нормальным номером 1-12 для января-декабря.
LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ; // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.
Или, лучше, использовать предварительно определенные объекты перечисления Month
, по одному на каждый месяц года. Совет: используйте эти Month
объекты по всей вашей кодовой базе, а не просто целое число, чтобы сделать ваш код более самодокументируемым, обеспечить допустимые значения и обеспечить безопасность типов .
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
Неизменяемые коллекции
Списки, наборы и карты, представленные выше, в идеале должны быть неизменяемыми коллекциями, так как изменение членства в этих коллекциях может привести к путанице и ошибочности.
Новый синтаксис Java 9 List.of
и Map.of
уже обещан неизменным. Однако в нашем случае Map
в идеале должно быть EnumMap
для эффективности производительности и памяти. Текущая реализация Map.of
и Set.of
, по-видимому, не обнаруживает использование перечислений в качестве элементов и автоматически оптимизируется при внутреннем использовании EnumMap
и EnumSet
. Существует проблема OpenJDK, открытая для рассмотрения таких проблем: учитывает усовершенствования EnumMap и EnumSet .
Один из способов получения неизменяемого EnumSet и неизменяемого EnumMap - через библиотеку Google Guava :
Результаты EACh использовать базовый EnumSet
/ EnumMap
. Такие операции, как get
и put
выдают исключение. Таким образом, вы получаете оптимизацию, связанную с enum, и неизменяемость.
Вот классы Season
и Group
, которые мы видели выше, модифицированные для использования библиотеки Google Guava 23.6.
Season
с неизменяемостью
package com.basilbourque.watering;
import java.time.LocalDate;
import java.time.Month;
import java.util.EnumSet;
import java.util.List;
public enum Season
{
SPRING( List.of( Month.MARCH , Month.APRIL ) ), // `List.of` provides literals-style syntax, and returns an immutable `List`. New in Java 9.
SUMMER( List.of( Month.MAY , Month.JUNE, Month.JULY , Month.AUGUST ) ),
FALL( List.of( Month.SEPTEMBER , Month.OCTOBER ) ),
WINTER( List.of( Month.NOVEMBER , Month.DECEMBER , Month.JANUARY , Month.FEBRUARY ) );
private List< Month > months;
// Constructor
Season ( List < Month > monthsArg )
{
this.months = monthsArg;
}
public List < Month > getMonths ( )
{
return this.months;
}
// For any given month, determine the season.
static public Season ofLocalMonth ( Month monthArg )
{
Season s = null;
for ( Season season : EnumSet.allOf( Season.class ) )
{
if ( season.getMonths().contains( monthArg ) )
{
s = season;
break; // Bail out of this FOR loop.
}
}
return s;
}
// For any given date, determine the season.
static public Season ofLocalDate ( LocalDate localDateArg )
{
Month month = localDateArg.getMonth();
Season s = Season.ofLocalMonth( month );
return s;
}
// Run `main` for demo/testing.
public static void main ( String[] args )
{
// Dump all these enum objects to console.
for ( Season season : EnumSet.allOf( Season.class ) )
{
System.out.println( "Season: " + season.toString() + " = " + season.getMonths() );
}
}
}
Group
с неизменяемостью
package com.basilbourque.watering;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.time.DayOfWeek;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
public enum Group
{
A(
Maps.immutableEnumMap(
Map.of( // `Map.of` provides literals-style syntax, and returns an immutable `Map`. New in Java 9.
Season.SPRING , Sets.immutableEnumSet( DayOfWeek.TUESDAY , DayOfWeek.THURSDAY ) ,
Season.SUMMER , Sets.immutableEnumSet( EnumSet.allOf( DayOfWeek.class ) ) ,
Season.FALL , Sets.immutableEnumSet( DayOfWeek.TUESDAY , DayOfWeek.THURSDAY ) ,
Season.WINTER , Sets.immutableEnumSet( DayOfWeek.TUESDAY )
)
)
),
B(
Maps.immutableEnumMap(
Map.of(
Season.SPRING , Sets.immutableEnumSet( DayOfWeek.FRIDAY ) ,
Season.SUMMER , Sets.immutableEnumSet( EnumSet.allOf( DayOfWeek.class ) ) ,
Season.FALL , Sets.immutableEnumSet( DayOfWeek.TUESDAY , DayOfWeek.FRIDAY ) ,
Season.WINTER , Sets.immutableEnumSet( DayOfWeek.FRIDAY )
)
)
),
C(
Maps.immutableEnumMap(
Map.of(
Season.SPRING , Sets.immutableEnumSet( DayOfWeek.MONDAY ) ,
Season.SUMMER , Sets.immutableEnumSet( EnumSet.allOf( DayOfWeek.class ) ) ,
Season.FALL , Sets.immutableEnumSet( DayOfWeek.MONDAY , DayOfWeek.TUESDAY ) ,
Season.WINTER , Sets.immutableEnumSet( DayOfWeek.MONDAY )
)
)
),
D(
Maps.immutableEnumMap(
Map.of(
Season.SPRING , Sets.immutableEnumSet( DayOfWeek.WEDNESDAY , DayOfWeek.FRIDAY ) ,
Season.SUMMER , Sets.immutableEnumSet( EnumSet.allOf( DayOfWeek.class ) ) ,
Season.FALL , Sets.immutableEnumSet( DayOfWeek.FRIDAY ) ,
Season.WINTER , Sets.immutableEnumSet( DayOfWeek.WEDNESDAY )
)
)
),
E(
Maps.immutableEnumMap(
Map.of(
Season.SPRING , Sets.immutableEnumSet( DayOfWeek.TUESDAY ) ,
Season.SUMMER , Sets.immutableEnumSet( EnumSet.allOf( DayOfWeek.class ) ) ,
Season.FALL , Sets.immutableEnumSet( EnumSet.of( DayOfWeek.TUESDAY , DayOfWeek.WEDNESDAY ) ) ,
Season.WINTER , Sets.immutableEnumSet( DayOfWeek.WEDNESDAY )
)
)
);
private Map < Season, Set < DayOfWeek > > map;
// Constructor
Group ( Map < Season, Set < DayOfWeek > > mapArg )
{
this.map = mapArg;
}
// Getter
private Map < Season, Set < DayOfWeek > > getMapOfSeasonToDaysOfWeek ( )
{
return this.map;
}
// Retrieve the DayOfWeek set for this particular Group.
public Set < DayOfWeek > daysForSeason ( Season season )
{
Set < DayOfWeek > days = this.map.get( season ); // Retrieve the value (set of days) for this key (a season) for this particular grouping of lawns/yards.
return days;
}
// Run `main` for demo/testing.
public static void main ( String[] args )
{
// Dump all these enum objects to console.
for ( Group group : EnumSet.allOf( Group.class ) )
{
System.out.println( "Group: " + group.toString() + " = " + group.getMapOfSeasonToDaysOfWeek() );
}
}
}
О java.time
Фреймворк java.time встроен в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date
, Calendar
и & SimpleDateFormat
.
Проект Joda-Time , теперь в режиме обслуживания , рекомендует перейти на классы java.time .
Чтобы узнать больше, см. Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .
Где получить классы java.time?
Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти некоторые полезные классы, такие как Interval
, YearWeek
, YearQuarter
и more .