I have 3 classes, Human, Date, and Zodiac. In Date I have two int types, month and day. I have normal constructor and getter. In Human I have a String name and a birthday from the type Date.
My Class Date:
public class Date {
private int month;
private int day;
public Date(int month, int day) {
this.month = month;
this.day = day;
}
public int getMonth() { return month;}
public int getDay() {return day;}
My Class Human
public class Human {
private String name;
private Date birthday;
public Human(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() { return name;}
public BirthDate getBirthday() { return birthday;}
In My class Zodiac I have a Main where I created some objects. Then I have a method zodiacToHuman were I give a Human his star sign. But this method didn't work at all. The method has a List as Parameter and returns a Map.
My method in class Zodiac:
public static Map<Human, String> zodiacToHuman(List<Human> humanlist){
Map<Human, String> personSign = new HashMap<>();
Human human;
String sign = "";
int day = Date.getDay();
int month = Date.getMonth();
if (month == 1) {
if (day < 20)
sign = "capricornus";
humanSign.put(human, sign);
else
sign = "aquarius";
humanSign.put(human, sign);
}//and so on
}
This is the error I get:
Non-static method 'getDay()' cannot be referenced from a static context
Non-static method 'getMonth()' cannot be referenced from a static context
Variable Human might not have been initialized
Can someone help me?
You can't do
int day = Date.getDay()
Create an object first of the Date class and use it to get the day and month
Data date = new Date()
int day = date.getDay()
int month = date.getMonth()
Also you haven't initialised your Human class object.
You can write
Human human = new Human(some_day, some_month)
Let's change the name of Date to more precise MonthWithDay.
We can shorten the code of that class by making it a record. By default, the implicitly created getter methods are named the same as the member field name.
public record MonthWithDay( int month , int day ) { }
Similarly we can define your Human class as a record in one short line.
public record Human( String name , MonthWithDay monthDayWhenBorn ) { }
Regarding your method to determine zodiac:
public static Map<Human, String> zodiacToHuman(List<Human> humanlist){ …
… there is no need for static. In your scenario, that seems like a reasonable feature on your Human class.
Tip: In object-oriented programming, using static is not object-oriented. Try to minimize use of static. Use as a last resort only.
public record Human( String name , MonthWithDay monthDayWhenBorn )
{
public String zodiac ( )
{
int day = this.monthDayWhenBorn.day();
int month = this.monthDayWhenBorn.month();
if ( month == 1 )
{
if ( day < 20 )
{ return "capricornus"; }
else
{ return "aquarius"; }
}
return "other";
}
}
Populate some example data.
List < Human > humans =
List.of(
new Human( "Alice" , new MonthWithDay( 1 , 11 ) ) ,
new Human( "Alice" , new MonthWithDay( 1 , 22 ) ) ,
new Human( "Carol" , new MonthWithDay( 11 , 27 ) )
);
Create your map of human to zodiac.
Map< Human , String > mapOfHumanToZodiac = new HashMap<>() ;
Loop through each Human object, interrogate for its zodiac, and place into our map.
for ( Human human : humans )
{
mapOfHumanToZodiac.put( human , human.zodiac() );
}
Dump to console.
System.out.println( "mapOfHumanToZodiac = " + mapOfHumanToZodiac );
mapOfHumanToZodiac = {Human[name=Alice, monthDayWhenBorn=MonthWithDay[month=1, day=11]]=capricornus, Human[name=Alice, monthDayWhenBorn=MonthWithDay[month=1, day=22]]=aquarius, Human[name=Carol, monthDayWhenBorn=MonthWithDay[month=11, day=27]]=other}
By the way, in real work we would define an enum to represent each of the zodiac signs rather than use mere strings. Doing so provides type-safety, ensures valid values (avoids errors from typos in the strings), and makes the code more self-documenting.
java.time
Java comes with an industry-leading framework of date-time classes, found in the java.time package. These classes include a MonthDay class. So no need to invent your own. We can delete your MonthWithDay class.
Tweak the Human class.
public record Human( String name , MonthDay monthDayWhenBorn ) // <-- Use java.time.MonthDay class.
{
public String zodiac ( )
{
int day = this.monthDayWhenBorn.getDayOfMonth(); // <-- Use java.time.MonthDay class.
int month = this.monthDayWhenBorn.getMonthValue(); // <-- Use java.time.MonthDay class.
if ( month == 1 )
{
if ( day < 20 )
{ return "capricornus"; }
else
{ return "aquarius"; }
}
return "other";
}
}
Change how we create the sample data.
List < Human > humans =
List.of(
new Human( "Alice" , MonthDay.of( 1 , 11 ) ) , // <-- Use java.time.MonthDay class.
new Human( "Alice" , MonthDay.of( 1 , 22 ) ) ,
new Human( "Carol" , MonthDay.of( 11 , 27 ) )
);
And we get the same results.
Errors and reasons
Variable Human might not have been initialized
Its not a error its a warning that saying human variable might be null as you have only decalre the variable human. To initialize either you need to create an instance or assign null to it
Human human = new Human(YOUR VALUES);
//or
Human human = null;
Non-static method 'getDay()' cannot be referenced from a static context
Non-static method 'getMonth()' cannot be referenced from a static context
You cannot access public methods of a class directly without creating an object.
NOTE
As per my understanding you are giving each human a sign value.You can achive the same while you are creating each human object and later on create a map from it.
Eg:
public class Human {
private String name;
private Date birthday;
private String sign;
public Human(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
assignZodiac();
}
private void assignZodiac(){
String sign = "";
//getting birhday month and day values
int day = birthday.getDay();
int month = birthday.getMonth();
// your logic of assignment
if (month == 1) {
if (day < 20)
sign = "capricornus";
else
sign = "aquarius";
}//and so on
}
//getter setter
}
Now you can create a map from the list. eg:
// human list has been already created
Map<Human,String> humanSign=newHasmap<>();
for(Human human : humanList) {
humanSign.put(human,human.getSign()) //asuming getSign is the getter for sign filed in Human.
}
Also I would suggest you to change Date class name to something else since java already has a class of the same name. It's just for naming convection
As I understand the humanList contains entries of Human Objects.
You should try iterating over the list, like so
public static Map<Human, String> zodiacToHuman(List<Human> humanlist) {
Map<Human, String> personSign = new HashMap<>();
for (Human human : humanList) {
String sign = "";
int day = human.getBirthday().getDay();
int month = human.getBirthday().getMonth();
if (month == 1) {
if (day < 20) {
sign = "capricornus";
} else {
sign = "aquarius";
}
} //and so on
humanSign.put(human, sign);
}
}
Related
Map<LocalDate, Set<Meeting>> meetings = new HashMap<LocalDate, Set<Meeting>>();
I have a hashmap that stores the date as key and Set of Meeting Class as value.
Collection<Set<Meeting>> insideMeeting = meetings.values();
System.out.println("meetings and their dates " + Arrays.asList(meetings));
System.out.println("How many meetings are there for date? " + insideMeeting);
for (Iterator<Set<Meeting>> iterator = insideMeeting.iterator(); iterator.hasNext();) {
System.out.println("value= " + iterator.next());
}
And I am trying to access the said Meeting classes with the same date inside the collection set and compare them.
public class Meeting implements Comparable<Meeting>{
private String employeeId;
private LocalTime startTime;
private LocalTime finishTime;
public Meeting(String employeeId, LocalTime startTime, LocalTime finishTime) {
this.employeeId = employeeId;
this.startTime = startTime;
this.finishTime = finishTime;
}
public String getEmployeeId() {
return employeeId;
}
public LocalTime getStartTime() {
return startTime;
}
public LocalTime getFinishTime() {
return finishTime;
}
public int compareTo(Meeting that) {
Interval meetingInterval = new Interval(startTime.toDateTimeToday(),
finishTime.toDateTimeToday());
Interval toCompareMeetingInterval = new Interval(that.getStartTime().
toDateTimeToday(), that.getFinishTime().toDateTimeToday());
if(meetingInterval.overlaps(toCompareMeetingInterval)){
return 0;
}else{
return this.getStartTime().compareTo(that.getStartTime());
}
}
}
Basically, if I could I want to compare the meetings (so there are no overlapping meetings in a single day. Let's say someone booked a room for a meeting at 9 am - 11 am and someone else booked the meeting from 10 am - 11 am. We wouldn't want the second request.) Do something like for each set of meetings in the collection I want to compare them with each other. I do have a compareTo method in the Meeting class.
DEBUG HERE!
meetings and their dates [{2011-03-21=[office.Meeting#4690b489, office.Meeting#363ee3a2]}]
How many meetings are there for date? [[office.Meeting#4690b489, office.Meeting#363ee3a2]]
value= [office.Meeting#4690b489, office.Meeting#363ee3a2]
for (var entry : meetings.values()) {
for (Meeting meeting : entry) {
//do whatever with meeting here
}
}
This is how you would loop through each set in your map, and then through each Meeting in your set. It's important to remember that your map will contain several collections, and so you must access one using a key or loop through all of them like here.
This is how I'd personally do it if you really wanted to use an iterator.
Iterator<Meeting> it = insideMeeting.iterator();
while(it.hasNext()){
Meeting current = it.next();
it.forEachRemaining(testMeeting -> {
if(current.getStartTime() == testMeeting.getStartTime() ||
current.getFinishTime() == testMeeting.getStartTime() /*Add rest of conditions*/){
//.remove whichever one you want
}
});
}
Although, as #studmoobs pointed out this is a bit complicated. I'm sure there'd be a better way of doing it without specifically a set. Like for example any indexed list.
I'm writing a program to sort datetimes by date.
There is a DateTime beginning and a DateTime end.
These are put into their own object from user input.
The DateTime beginning and the DateTime end make up their own term.
So each term has an object of its own with a DateTime beginning and a DateTime end.
What I want to do is to sort all DateTime beginning and all DateTime end by date.
How can I do this? I'm thinking about a comperator but I can't seem to do this on custom objects.
So lets say user imputs one beginning date of 01/01/2000 and one end date of 01/01/2002. This makes up one term.
The user then imputs a second term consisting of a beginning date of 01/01/2001 and an end date of 01/01/2003.
What I now want to do is to sort the dates and make the three new terms which would be:
beginning 01/01/2000 end 01/01/2001
beginning 01/01/2001 end 01/01/2002
beginning 01/01/2002 end 01/01/2003
I'm stuck on how to proceed with this, any ideas?
Put every date in a new collection, sort it by the date, and then create new objects that consist neighbour dates from the collection.
Try:
public static void main(String[] args) {
List<YourClass> list = new ArrayList<>();
list.add(new YourClass(new Date(100000000), new Date(200000000)));
list.add(new YourClass(new Date(150000000), new Date(250000000)));
list.add(new YourClass(new Date(50000000), new Date(300000000)));
System.out.println(list);
List<Date> dates = new ArrayList<>();
for (YourClass yc : list){
if (!dates.contains(yc.beginning)) dates.add(yc.beginning);
if (!dates.contains(yc.end)) dates.add(yc.end);
}
Collections.sort(dates);
List<YourClass> list2 = new ArrayList<>();
for (int i=0; i < dates.size() -1; i++){
list2.add(new YourClass(dates.get(i), dates.get(i+1)));
}
System.out.println(list2);
}
public static class YourClass {
Date beginning;
Date end;
public YourClass(Date beginning, Date end) {
this.beginning = beginning;
this.end = end;
}
#Override
public String toString() {
return "\n" + beginning + " -> " + end ;
}
}
tl;dr
What I want to do is to sort all DateTime beginning and all DateTime end by date.
You can do one or the other but not both.
To sort by start date (seems sensible in practice), implement compareTo method.
return this.getDateRange().getStart().compareTo( thatStart );
To sort by stop date (I do not see any sense in this), implement the Comparator interface.
return
t1.getDateRange().getEnd().compareTo(
t2.getDateRange().getEnd()
)
;
LocalDate
As others noted, you should be using the modern java.time classes, never the terrible old Date/Calendar/SimpleDateFormat classes. For a date-only value, without time-of-day and without time zone, use LocalDate.
LocalDateRange
As the Answer by jbx discusses, you should represent your term’s start and stop dates as a pair. But do not write a class when one already exists. Use LocalDateRange class from the ThreeTen-Extra project. This project adds functionality to the java.time classes.
Comparable
On your Term class, implement Comparable interface to enable simple easy sorting. Add the method compareTo. The obvious approach there would be to compare the starting LocalDate of each Term object’s LocalDateRange object.
The LocalDate class implements compareTo, no we don’ have to.
#Override
public int compareTo ( Object o ) {
if ( this == o ) return 0;
if ( o == null || getClass() != o.getClass() ) return 0;
LocalDate thatStart = ( ( Term ) o ).getDateRange().getStart();
return this.getDateRange().getStart().compareTo( thatStart );
}
See the Java Tutorial on object-ordering.
Sort by stop date
Your Question is not clear, but you seem to be asking to alternatively sort by the ending date. I cannot imagine how this is useful in practical terms. But anyways, the solution is to sort by providing an implementation of the Comparator interface.
#Override
public int compare ( Term t1 , Term t2 ) {
return t1.getDateRange().getEnd().compareTo( t2.getDateRange().getEnd() );
}
Example class
Here is an example Term class. May not be production-quality code, but should get you going in the right direction.
package com.basilbourque.example;
import org.threeten.extra.LocalDateRange;
import java.time.LocalDate;
import java.time.Month;
import java.util.*;
public class Term implements Comparable {
private UUID id;
private LocalDateRange dateRange;
// Constructor
public Term ( LocalDate start , LocalDate stop , UUID id ) {
Objects.requireNonNull( start ); // TODO: Add more such checks for all arguments.
if ( start.getYear() < 2015 ) { // TODO: Add more such checks for too far into the past or future, for both start and for stop.
throw new IllegalArgumentException( "Year of start date is too far in the past. Message # afcd30a0-b639-4ccf-b064-18cc2ea8587b." );
}
this.id = id;
this.dateRange = LocalDateRange.of( start , stop );
}
// Alternative constructor.
public Term ( LocalDateRange dateRange , UUID id ) {
this( dateRange.getStart() , dateRange.getEnd() , id );
}
// --------| Object |-------------------------
#Override
public String toString ( ) {
return "Term{ " +
"id=" + id +
" | dateRange=" + dateRange +
" }";
}
public UUID getId ( ) {
return id;
}
public LocalDateRange getDateRange ( ) {
return dateRange;
}
#Override
public boolean equals ( Object o ) {
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
Term term = ( Term ) o;
return this.getId().equals( term.getId() );
}
#Override
public int hashCode ( ) {
return Objects.hash( this.getId() );
}
#Override
public int compareTo ( Object o ) {
if ( this == o ) return 0; // If same object.
if ( o == null || getClass() != o.getClass() ) return 0;
LocalDate thatStart = ( ( Term ) o ).getDateRange().getStart();
return this.getDateRange().getStart().compareTo( thatStart );
}
static public class StopDateComparator implements Comparator < Term > {
#Override
public int compare ( Term t1 , Term t2 ) {
return t1.getDateRange().getEnd().compareTo( t2.getDateRange().getEnd() );
}
}
}
Try it.
public static void main ( String[] args ) {
Term t1 = new Term( LocalDate.of( 2018 , Month.JUNE , 23 ) , LocalDate.of( 2018 , Month.JULY , 23 ) , UUID.randomUUID() );
Term t2 = new Term( LocalDate.of( 2018 , Month.JANUARY , 23 ) , LocalDate.of( 2018 , Month.DECEMBER , 23 ) , UUID.randomUUID() );
Term t3 = new Term( LocalDate.of( 2018 , Month.MARCH , 23 ) , LocalDate.of( 2018 , Month.APRIL , 23 ) , UUID.randomUUID() );
List < Term > terms = new ArrayList <>( List.of( t1 , t2 , t3 ) );
System.out.println( "Before natural sort: " + terms );
Collections.sort( terms );
System.out.println( "After natural sort: " + terms );
Collections.sort( terms , new Term.StopDateComparator() );
System.out.println( "After Comparator sort: " + terms );
}
Before natural sort: [Term{ id=27c0b9e6-076f-4ded-9bbd-bf1a2c7914bc | dateRange=2018-06-23/2018-07-23 }, Term{ id=792bf365-eca4-460b-afad-c5cf62cf9a29 | dateRange=2018-01-23/2018-12-23 }, Term{ id=c49f79e1-11cd-4865-aa46-8fbf3c85dbfd | dateRange=2018-03-23/2018-04-23 }]
After natural sort: [Term{ id=792bf365-eca4-460b-afad-c5cf62cf9a29 | dateRange=2018-01-23/2018-12-23 }, Term{ id=c49f79e1-11cd-4865-aa46-8fbf3c85dbfd | dateRange=2018-03-23/2018-04-23 }, Term{ id=27c0b9e6-076f-4ded-9bbd-bf1a2c7914bc | dateRange=2018-06-23/2018-07-23 }]
After Comparator sort: [Term{ id=c49f79e1-11cd-4865-aa46-8fbf3c85dbfd | dateRange=2018-03-23/2018-04-23 }, Term{ id=27c0b9e6-076f-4ded-9bbd-bf1a2c7914bc | dateRange=2018-06-23/2018-07-23 }, Term{ id=792bf365-eca4-460b-afad-c5cf62cf9a29 | dateRange=2018-01-23/2018-12-23 }]
abuts
If your Term objects should run up against one another in succession, you can test for that using the LocalDateRange::abuts method.
The approach in comparing is Half-Open, where the beginning is inclusive while the ending is exclusive. So a year starts on the first of the year and runs up to, but does not include, the first of the following year. You show this in your examples in the Question.
I assume the question is not just about sorting, but also about splitting overlapping intervals into smaller segments. You have to play a lot with Interval Arithmetic.
With Java 8 you can start by encoding your 'terms' as a time Interval, which in itself is Comparable. The second part would be to split your intervals into multiple ones if the user specifies overlapping ones.
class Interval implements Comparable<Interval> {
private final LocalDateTime start;
private final LocalDateTime end;
public Interval(LocalDateTime start, LocalDateTime end) {
this.start = start;
this.end = end;
}
public int compareTo(Interval that) {
return this.start.compareTo(that.start);
}
public boolean overlaps(Interval that) {
return !this.isBefore(that) && !this.isAfter(that);
}
public boolean contains(Interval that) {
return this.start.isBefore(that.start) && this.end.isAfter(that.end);
}
public boolean isBefore(Interval that) {
return this.end.isBefore(that.start);
}
public boolean isAfter(Interval that) {
return this.start.isAfter(that.end);
}
public Set<Interval> fragment(Interval that) {
if (that.start.isBefore(this.start)) {
return that.fragment(this);
}
Set<Interval> result = new HashSet<>();
if (this.end.isBefore(that.start)) {
result.add(this);
result.add(that);
result.add(new Interval(this.end, that.start));
} else if ((this.end.isAfter(that.start) && this.end.isBefore(that.end)) {
result.add(new Interval(this.start, that.start);
result.add(new Interval(that.start, this.end);
result.add(new Interval(this.end, that.end));
} else if (this.end.isAfter(that.end)) {
result.add(new Interval(this.start, that.start);
result.add(new Interval(that);
result.add(new Interval(that.end, this.end));
}
}
}
You can now keep them sorted, since Intervals are comparable by start date.
Whenever the user enters a new Interval (term) you have to go through the list and checks whether it contains() the existent interval, or whether it comes before it, with isBefore() or isAfter(). If it overlaps() you have to be careful if to also check whether it overlaps with the next interval in the list.
You can then call fragment() which will combine the 2 intervals together into smaller ones. You need to be careful to remove the previous ones. So maybe makes sense to just go through the list and check if they overlap or not. If you reach the end you can still use fragment() to combine two disjoint intervals.
Can someone help me with this? I can't find any example..
I've created a Java Class called Subtaks with multiple fields. And i'm trying to sort a list of Subtasks. Firt I'm sorting the list alphabetically with one field called status; but if this field is the same i'm trying to sort it by another String field that containt a date with this format:
String resolutionDate = "18/09/2014"
I've implemented this method to sort it:
#Override
public int compareTo(Subtask o) {
if (this.status.equals(o.status))
return o.resolutiondate.compareTo(this.resolutiondate);
else
return this.status.compareTo(o.status);
}
I assume you want to sort by date so you'd either have to manually extract year, day and month and compare them or parse the date and compare the parsed dates.
For the first approach, you could use something like this:
String resolutionDateReordered = resolutionDate.replaceAll("(\\d+)/(\\d+)/(\\d+)","$3$2$1");
And then compare resolutionDateReordered. This assumes that the parts of the date have equal length, i.e. 2-digit day and month and 4 digit year and would reorder the string "18/09/2014" to "20140918", which then would be sortable by date.
This could be optimized by creating the pattern once and reusing it, e.g.
Pattern p = Pattern.compile( "(\d{2})/(\d{2})/(\d{4})" ); //pattern slightly changed to expect 2-digit day and month and 4-digit year.
and in your compare() method:
String resolutionDateReordered = p.matcher( resolutionDate ).replaceAll( "$3$2$1" );
As for the second approach you do something like this:
DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
Date resolutionDateParsed = df.parse( resolutionDate );
And then compare resolutionDateParsed using Date's compareTo() method.
Depending on the size of the list, the first or second approach might be faster, but YMMV. For example, on my machine sorting a list of date strings having your format using the optimized version of the string reordering is approx. twice as fast as parsing first and comapring the dates, with 3.5 seconds vs. 7.4 seconds for 100k entries.
First you convert your String value of date("dd/mm/yyyy") to type of Date(java.util)
public class YourClassName implements Comparable<YourClassName> {
....
//return -1 for less than, 0 for equals, and 1 for more than
public compareTo(Subtask subTask) {
int result = 0;
result = getResolutionDate().compareTo(subTask.getResolutionDate());
if (result != 0)
{
return result;
}
result = getStatus().compareTo(subTask.getStatus());
if (result != 0)
{
return result;
}
...
}
}
Here is the SubTask class:
package com.test.main;
import java.util.Date;
public class Subtask implements Comparable<Subtask> {
private String status;
private Date resolutionDate;
public Subtask(String status, Date resolutionDate) {
super();
this.status = status;
this.resolutionDate = resolutionDate;
}
public String getStatus() {
return status;
}
public Date getResolutionDate() {
return resolutionDate;
}
#Override
public int compareTo(Subtask o) {
int compareToIgnoreCase = status.compareToIgnoreCase(o.getStatus());
return compareToIgnoreCase==0?resolutionDate.compareTo(o.getResolutionDate()):compareToIgnoreCase;
}
}
Main method:
public static void main(String args[]){
List<Subtask> arrayList = new ArrayList<>();
Calendar instance = Calendar.getInstance();
instance.setTime(new Date());
arrayList.add(new Subtask("test1", instance.getTime()));
instance.set(Calendar.MONTH, 11);
arrayList.add(new Subtask("test1", instance.getTime()));
instance.set(Calendar.MONTH, 5);
arrayList.add(new Subtask("test1", instance.getTime()));
Collections.sort(arrayList);
for (Subtask subtask : arrayList) {
System.out.println(subtask.getResolutionDate());
}
}
I have a string array
String[] weekDays
weekDays represents days of week. for eg
{1, 2, 4} means Monday, Tuesday, Thusday
{2,5,6,7} means Tuesday, Friday, Saturday, Sunday
I have seven boolean representing 7 days of week and have to set true or false on the basis of weekDays.
My code
private static void setWeekDays(final Object object, final String[] weekDays) {
for (String day : weekDays) {
if( day.equalsIgnoreCase("1")) {
object.setMonday(true);
} else if( day.equalsIgnoreCase("2")) {
object.setTuesday(true);
} else if( day.equalsIgnoreCase("3")) {
object.setWednesday(true);
} else if( day.equalsIgnoreCase("4")) {
object.setThrusday(true);
} else if( day.equalsIgnoreCase("5")) {
object.setFriday(true);
} else if( day.equalsIgnoreCase("6")) {
object.setSaturday(true);
} else if( day.equalsIgnoreCase("7")) {
object.setSunday(true);
}
}
}
But it has so many if else statements and before performing this i explicitly set all booleans to be false.
Is there any other simple way to do the same thing? Any suggestion ??
I think you can try to use ENUM for this.
For example
public enum WeekDay {
MONDAY("1");
private String value;
private WeekDay(String value) {
this.value = value;
}
public static WeekDay find(String value) {
for (WeekDay weekDay : values()) {
if (weekDay.value.equals(value)) {
return weekDay;
}
}
}
So then you can use this ENUM as field of your DTO.
Define an Enum to be your days of the week:
enum DayOfWeek {
MONDAY, TUESDAY, etc.
}
Create a map to go from string to day:
Map<String, DayOfWeek> dayByString = new HashMap<String, DayOfWeek>();
Fill the map with all lookups you want, i.e.
dayByString.put("1", DayOfWeek.MONDAY);
dayByString.put("2", DayOfWeek.TUESDAY);
Now to look up the day just do:
DayOfWeek day = dayByString.get(str);
This will return null if there is no match - or the matching day of the week.
Now instead of your 7 booleans use an EnumSet:
Set<DayOfWeek> days = new EnumSet<DayOfWeek>(DayOfWeek.class);
Internally that will use a bitfield to represent the days so will be incredibly fast and space efficient.
To set the flag do days.add(day);
To unset it do days.remove(day);
To check if it is set do days.contains(day); or days.contains(DayOfWeek.MONDAY);.
private static void setWeekDays(EnumSet<DayOfWeek> set, final String[] weekDays) {
set.clear();
for (String day : weekDays) {
set.add(dayByString.get(day));
}
}
You are done, that's all you need.
EnumSet is the correct way to store this. There are other options but 7 booleans is flat out wrong.
You can try following:
Add following code in your DTO Object
List weekDaysList;
private void setWeekDays(String[] weekDays){
weekDaysList = Arrays.asList(weekDays);
}
private boolean isWeekdaySet(String weekday){
if (weekDaysList == null || weekDaysList.size() == 0){
return false;
}
return weekDaysList.contains(weekday);
}
update each of the getMonday, getTuesday method as follows:
public boolean getMonday() {
return isWeekdaySet("1");
}
public boolean getTuesday(){
return isWeekdaySet("2");
}
I hope this helps.
If the DTO class can NOT be modified, you could try to make a method mapping previously, and use reflection to call the target method, like this:
private static Method[] methods;
private static void init() throws Exception {
Class klass = BusTravelDetailDTO.class;
String[] methodNames = new String[]{null, "setMonday", "setTuesday", "setSunday"};
methods = new Method[methodNames.length];
for (int i = 0; i < methods.length; i++) {
if(methodNames[i] != null) {
methods[i] = klass.getMethod(methodNames[i], Boolean.class);
}
}
}
private static void setWeekDays(final Object object, final String[] weekDays) {
for (String day : weekDays) {
methods[Integer.parseInt(day)].invoke(object, Boolean.TRUE);
}
}
But, since you have only seven choices, if-else might be the most simple and efficient way.
If the DTO class can be modifie, use enum instead of seven boolean flags.
I'm learning Java and am at the edge of my understanding with this. I feel I'm not far off, but can't quite get there. What I need to do is, generate a random date of birth and pass it into a variable so I can use it in another class.
However, I also need to change the year, based on 4 different age groups.
My standalone class to generate the DOB and pass into the variable is as follows. Can anyone help me over the top by tidying and suggesting what's wrong?:
package domainentities;
import java.util.GregorianCalendar;
public class RandomDateOfBirthGenerator {
private static String getDateOfBirth(String dateOfBirth) {
return dateOfBirth;
}
public static enum Mode {
SENIOR, ADULT, YOUTH, CHILD
}
public static void generateRandomDateOfBirth(Mode mode){
GregorianCalendar gc = new GregorianCalendar();
int year = 0;
String dateOfBirth;
switch(mode){
case SENIOR:
year = randBetween(1900, 1940);
break;
case ADULT:
year = randBetween(1941, 1995);
break;
case YOUTH:
year = randBetween(1995, 2002);
break;
case CHILD:
year = randBetween(2002, 2014);
break;
}
gc.set(gc.YEAR, year);
int dayOfYear = randBetween(1, gc.getActualMaximum(gc.DAY_OF_YEAR));
gc.set(gc.DAY_OF_YEAR, dayOfYear);
dateOfBirth = gc.get(gc.DAY_OF_MONTH) + "/" + gc.get(gc.MONTH) + "/" + gc.get(gc.YEAR);
}
public static int randBetween(int start, int end) {
return start + (int)Math.round(Math.random() * (end - start));
}
}
The code seems OK, however I'd suggest you to put data into enum and avoid switch:
public static enum Mode {
SENIOR(1900, 1940), ADULT(1941, 1995), YOUTH(1995, 2002), CHILD(2002, 2014);
private final int from;
private final int to;
Mode(int from, int to) {
this.from = from;
this.to = to;
}
public int from() {return from;}
public int to() {return to;}
}
public static int generateRandomDateOfBirth(Mode mode){
return randBetween(mode.from(), mode.to());
}
Looks simpler, doesn't it?
BTW if you are going forward from your exercise think about design. Indeed your program is good for year 2014 only. It will require modifications on year 2015. So, probably you should store minAge and maxAge instead of years.
There are some problems with your code.
private static String getDateOfBirth(String dateOfBirth) {
return dateOfBirth;
}
This is a private methode, which will return the methode parameter, so it does nothing. You should change the return type of generateRandomDateOfBirth and return the created date-string:
public static String generateRandomDateOfBirth(Mode mode){
String dateOfBirth;
... //Create dateOfBirth
return dateOfBirth;
}
Also, you are not setting the Month (only day and year). Where are you calling the RandomDate class and what are the problems?