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.
Related
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);
}
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
This is the class
public class Book {
private String name;
private int publicationYear;
public Book(String name, int publicationYear) {
this.name = name;
this.publicationYear = publicationYear;
}
public boolean equals(Book compared) {
if (this == compared) {
return true;
}
if (!(compared instanceof Book)) {
return false;
}
Book comparedBook = (Book) compared;
if (this.name.equals(comparedBook.name)
&& this.publicationYear == comparedBook.publicationYear) {
return true;
}
return false;
}
}
I tried doing this in the main
while (true) {
System.out.println("Name (empty will stop):");
String name = scanner.nextLine();
if (name.isEmpty()) {
break;
}
System.out.println("Publication year:");
int publicationYear = Integer.valueOf(scanner.nextLine());
Book book = new Book(name, publicationYear);
if (!(books.contains(book))) {
books.add(book);
}
So if the user keeps giving a book with the same name and year the program adds them to the list still
You can’t change the signature of Object.equals(Object).
You should use the #Override annotation to catch this kind of error. See tutorial by Oracle.
#Override
public boolean equals(Object compared) {
if (this == compared) {
return true;
}
if (!(compared instanceof Book)) {
return false;
}
Book comparedBook = (Book) compared;
if (this.name.equals(comparedBook.name)
&& this.publicationYear == comparedBook.publicationYear) {
return true;
}
return false;
}
The Answer by Elliott Frisch is correct and wise.
Alternatively, in Java 16+ you could avoid your issue of incorrectly writing the equals method by letting Java handle the implementation of equals.
record
Java 16 brought the records feature. A record is a brief way to write a class whose main purpose is to communicate data transparently and immutably. You simply declare the type and name of each member field. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.
And by the way, Java offers a class to represent a year: java.time.Year.
public record Book ( String title , Year published ) {}
The implicit methods such as equals are implemented to consider each and every member field.
new Book( "Free To Choose" , Year.of( 1990 ) )
.equals(
new Book( "The Foundation Trilogy" , Year.of( 1974 ) )
)
false
You can define a record locally, or nested or separately. Ditto for enum and for interfaces, in Java 16 and later.
Set
To collect objects while automatically eliminating duplicates, use a Set implementation.
Set< Book > books = new HashSet<>() ;
books.add( new Book( "Free To Choose" , Year.of( 1990 ) ) ) ;
books.add( new Book( "Free To Choose" , Year.of( 1990 ) ) ) ; // Duplicate! Not added, because a set does not allow duplicates.
books.add( new Book( "The Foundation Trilogy" , Year.of( 1974 ) ) ) ;
Interrogate for the size. You’ll find a size of two rather than three.
I have my object class :
public class Test {
private String name;
private int id;
private boolean aBoolean;
public Test(String name, int id, boolean aBoolean) {
this.name = name;
this.id = id;
this.aBoolean = aBoolean;
}
and 2 arrayList of this object:
ArrayList<Test> myFirstList = new ArrayList<>();
ArrayList<Test> mySecondList = new ArrayList<>();
myFirstList.add(new Test("a", 1, false));
myFirstList.add(new Test("b", 2, false));
myFirstList.add(new Test("c", 3, false));
mySecondList.add(new Test("a", 1, false));
mySecondList.add(new Test("b", 2, false));
mySecondList.add(new Test("c", 3, false));
now I need to check that these array list contain objects which have same fields inside; is there any ways but to use fori loop and getting each parameter for compare?
Record
Apparently you want to compare objects of the same class for equality by examining the content of each and every member field.
You get that behavior automatically by using the records feature. This feature is new in Java 16, with early access builds available now. In a record you simply declare the member fields. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.
So no need for you to override equals wih your own implementation as seen in the other Answers.
Use a record where the primary purpose of a class is to immutably and transparently carry data. In contrast, if your class has a focus on behavior with encapsulated data, or uses other OOP features such as inheritance, then your should use a regular class, not records.
So your code:
public class Test {
private String name;
private int id;
private boolean aBoolean;
public Test(String name, int id, boolean aBoolean) {
this.name = name;
this.id = id;
this.aBoolean = aBoolean;
}
… becomes:
public record Test ( String name , int id , boolean aBoolean ) {}
Using that record:
Test x = new Test ( "Alice" , 42 , true ) ;
Test y = new Test ( "Alice" , 42 , false ) ;
boolean same = x.equals( y ) ; // false because of third field.
Comparing lists
Use List#equals to see if two lists contain equal elements in the same order.
List x = List.of(
new Test ( "Alice" , 1 , true ) ,
new Test ( "Bob" , 2 , true ) ,
new Test ( "Carol" , 3 , true )
);
List y = List.of(
new Test ( "Alice" , 1 , true ) ,
new Test ( "Bob" , 2 , true ) ,
new Test ( "Carol" , 3 , true )
);
boolean same = x.equals( y ) ; // True.
If you override equals() in our class you should be able to sort both lists with myFirstList.sort() and then just use
myFirstList.equals(mySecondList)
This is not related to ArrayList, it is related to your Test Class. As said by #VGR, In order to know if the two objects of the Test Class have the same fields, you have to override the equals method inside your Test class like this:
public class Test {
// your constructors and methods
#Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Test)) {
return false;
}
Test t = (Test) o;
// Compare the data members and return accordingly
return this.name.equals(t.name)
&& this.id == t.id;
&& this.aBoolean == t.aBoolean;
}
}
Now to check if two objects in ArrayList are equal, Simply do this:
boolean result = myFirstList.get(indexOfObject1).equals(indexOfObject2);
System.out.println("Result : "+result);
And obviously, to search for equal objects in two ArrayLists, you have to loop through every single object in the first list, then compare that object with all objects in the second list, or implement your own algorithm. That's where data structure and algorithms are used!
What if I'll use switch in getByIntValue()? Is it really neccessary to use a SparseArray?
public enum Gender {
Unknown(0),
Male(1),
Female(2);
private static final SparseArray<Gender> lookupTable = new SparseArray<Gender>();
static {
for (final Gender gender : EnumSet.allOf(Gender.class)) {
lookupTable.put(gender.intValue, gender);
}
}
private final int intValue;
public static Gender getByIntValue(int val) {
return lookupTable.get(val);
}
private Gender(int intValue) {
this.intValue = intValue;
}
public int getIntValue() {
return intValue;
}
}
Since your int values go from 0 to 2, without hole, you could indeed simply use an array. A switch would also be fine, although it would probably be slightly slower than an array lookup. But unless you call the method billions of times, it won't make any noticeable difference. Use what you find the clearest and easiest to understand and maintain.
If you have posted realistic int values, then you don't need to set them explicitly on each enum member, and don't need switch. Just use
Gender.values()[intValue]
List.copyOf( EnumSet.allOf( Gender.class ) )
Caveat: This exercise in optimization seems silly for all but the most extreme scenario, as mentioned by JB Nizet. For real work, I would probably recommend the solution seen in the Answer by Marko Topolnik. But, for fun, I swung a bat at this ball.
Seems the goal is to render a static unmodifiable collection with very fast access by the given numbers 0, 1, 2.
As of Java 10, we have these new implemented (“default”) methods on the List interface: List.of & List.copyOf. These produce an unmodifiable collection. Though the backing implementation is undocumented and subject to change, I will assume it is something akin to an array with similar performance. Performance might even be faster than a conventional array, if the backing implementation detected the presence of an EnumSet and used some kind of bit vector.
I populate the List by passing an EnumSet to List.copyOf( Collection ).
So, this:
private static final SparseArray<Gender> lookupTable = new SparseArray<Gender>();
static {
for (final Gender gender : EnumSet.allOf(Gender.class)) {
lookupTable.put(gender.intValue, gender);
}
}
…becomes this:
private static final List < Gender > lookupTable = List.copyOf( EnumSet.allOf( Gender.class ) );
Entire class, with main for demo.
package com.basilbourque.example;
import java.util.EnumSet;
import java.util.List;
public enum Gender {
UNKNOWN( 0 ),
MALE( 1 ),
FEMALE( 2 );
private static final List < Gender > lookupTable = List.copyOf( EnumSet.allOf( Gender.class ) );
private final int intValue;
public static Gender getByIntValue ( int val ) {
return lookupTable.get( val );
}
public int getIntValue () {
return intValue;
}
// Constructor
private Gender ( int intValue ) {
this.intValue = intValue;
}
public static void main ( String[] args ) {
// Testing.
System.out.println( Gender.UNKNOWN.intValue );
System.out.println( Gender.getByIntValue( 0 ) );
System.out.println( "----" );
System.out.println( Gender.MALE.intValue );
System.out.println( Gender.getByIntValue( 1 ) );
System.out.println( "----" );
System.out.println( Gender.FEMALE.intValue );
System.out.println( Gender.getByIntValue( 2 ) );
}
}
When run.
0
UNKNOWN
1
MALE
2
FEMALE
By the way, as the biological default, FEMALE should come before MALE.
I have an Enum for Days of week (with Everyday, weekend and weekdays) as follows where each entry has an int value.
public enum DaysOfWeek {
Everyday(127),
Weekend(65),
Weekdays(62),
Monday(2),
Tuesday(4),
Wednesday(8),
Thursday(16),
Friday(32),
Saturday(64),
Sunday(1);
private int bitValue;
private DaysOfWeek(int n){
this.bitValue = n;
}
public int getBitValue(){
return this.bitValue;
}
}
Given a TOTAL of any combination of the values, what would be the simplest way to calculate all individual values and make an arraylist from it. For example given the number 56 (i.e. Wed+Thur+Fri), how to calculate the days.
The correct way to represent a collection of enum values is to use an EnumSet. This uses a bit vector internally. But exposing such an implementation detail as in your code is not a good idea. We're doing OO here, not bit-twiddling.
Additionally, you are mixing the concepts of a single value and a collection of values, which will likely lead to headaches down the road.
Example using the DayOfWeek enum built into Java 8 and later.
EnumSet<DayOfWeek> weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
Boolean isTodayWeekend = weekend.contains( LocalDate.now().getDayOfWeek() );
As Michael suggested do not expose this implementation detail to the outside world.
Create a static method that converts int bitmask to EnumSet:
public static EnumSet< DaysOfWeek > fromBitValues (
final int origBitMask
)
{
final EnumSet< DaysOfWeek > ret_val =
EnumSet.noneOf( DaysOfWeek.class );
int bitMask = origBitMask;
for ( final DaysOfWeek val : DaysOfWeek.values( ) )
{
if ( ( val.bitValue & bitMask ) == val.bitValue )
{
bitMask &= ~val.bitValue;
ret_val.add( val );
}
}
if ( bitMask != 0 )
{
throw
new IllegalArgumentException(
String.format(
"Bit mask value 0x%X(%d) has unsupported bits " +
"0x%X. Extracted values: %s",
origBitMask,
origBitMask,
bitMask,
ret_val
)
);
}
return ret_val;
}
You may also need a static method that converts an EnumSet to a bit mask, I leave this exercise to the reader.
Also, looking at your enum, Everyday, Weekends and Weekdays do not belong there. They are aggregates of you other DaysOfWeek values and as such should be defined as EnumSets.