Sorting a complete HashMap - java

I am trying to work with a seemingly complex HashMap object to pupulate my expandable Listview in android.
The generic parameters for my HashMap as as shown below:
//HashMap<Map<YEAR,MONTH>,List<DAYS>>
HashMap<Map<Integer,Integer>,List<Integer>
I am using the Hash map to monitor day, month and year when an event took place. So assuming an event took place on 12th, 20th and 25th in May 2013, I would do something as this:
HashMap<Integer,Integer>,List<Integer>> events = new HashMap<Integer,Integer>,List<Integer>();
HashMap<Integer,Integer> yearMonth = new HashMap<Integer,Integer>();
yearMonth.put(2013,5);
events.put(yearMonth,Arrays.asList(new Integer[]{12,20,25}));
I have created an adapter for my expandable list view and it displays fine as shown below. Now I want to be able to sort the above HashMap, first by Year and Month so that my listview will display events in 2014 at the top following by 2013, 2012 .... in that order.
Is this possible?
Thanks.

Well, I just read "sort a hasmap". In the case you really want sort your data, a hashmap is definitely the wrong one.
Maybe you should think about using a linkedlist...

Create your own class instead of the hashmap, and adjust the adapter to suit those objects.
Then you can implement your own sorting, by implementing Comparable and creating a compareTo() method in your class.
This gives you all the control you need. For example:
public class myEvent implements Serializable, Comparable<myEvent>
{
private Integer day;
private Integer month;
private Integer year;
public myEvent( Integer day, Integer month, Integer year, <your other data> )
{
// Save the stuff here
this.day = day;
this.month = month;
this.year = year;
}
// Create getDay(), getMonth(), getYear() methods for each parameter
public int compareTo( myEvent another )
{
// Here, compare the two events year by year, month by month, and day by day
if ( this.year.compareTo( another.getYear() ) == 0 )
{
if ( this.month.compareTo( another.getMonth() ) == 0 )
{
return this.day.compareTo( another.getDay() );
} else {
return this.month.compareTo( another.getMonth() );
}
} else {
return this.year.compareTo( another.getYear() );
}
}
}
EDIT: When you want to sort a list of those myEvent objects, you can use the Collection api to make use of the Comparable implementation:
List<myEvent> allevents = new ArrayList<myEvent>();
// Add to the list
...
// Now sort it.
Collections.sort( allevents );
Good luck.

Related

How to sort a list of objects

Supposing that I have a list of Test as below:
public class Test {
public static enum Unit {
DAY, WEEK, MONTH, YEAR /* , DAY_TO_DAY */
}
private int value;
private Unit unit;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Unit getUnit() {
return unit;
}
public void setUnit(Unit unit) {
this.unit = unit;
}
}
I will have a list of Test with the below values :
3 day,
5 month,
5 day,
6 year,
6 week,
12 week,
and our expection result as below :
3 day, 5 day, 6 week , 12 week, 5 month, 6 year
Currently, I create a code as below
Collections.sort(tests, new Comparator<Test >() {
#Override
public int compare(Test o1, Test o2) {
if(o1.getValue() > o2.getValue()) return 1;
if(o1.getValue() < o2.getValue()) return -1;
return 0;
}
However, it only sort by value and not by Unit.
The result with the above implementation is :
3 day, 5 day, 5 month, 6 week , 6 year, 12 week.
Please tell me know the way to satisfy two conditions (Unit and values) in this case.
Thanks.
You'll need to convert first into common format, and then compare. Since days are your smallest type, you should convert the two instances you're comparing into a number of days, and then compare based on that.
But you have some disambiguation to do first. How long is a month? Is it longer than 30 days, or shorter, or the same?
If you simply want to compare according to your example the following code solves the problem:
Collections.sort(terms, new Comparator<Test>() {
#Override
public int compare(final Test o1, final Test o2) {
int unitCompare = o1.getUnit().compareTo(o2.getUnit());
if (unitCompare == 0) {
return o1.getValue() - o2.getValue();
}
return unitCompare;
}
});
However, this code does NOT factor in that months can have different length and the code does NOT factor in that 35 days is longer than a month.
In order to do that i suggest that the classes from the java.time package (such as java.time.Period) are used to make proper comparisons (a good trail to learn about this can be found here). If Java 8 is not an option Joda Time provides the same type of functionality.
You'll first have to check on the basis of Unit and then on the basis of Value in the overridden compareTo(). See here the implementation of sorting on the basis of lastname and then firstname:http://docs.oracle.com/javase/tutorial/collections/interfaces/order.html
Use TimeUnit to replace your Unit enum.
Your Term class could therefore have a .toMillis() method which is very simple:
public final long toMillis()
{
return timeUnit.toMillis(value);
}
You can then just Long.compare(o1.toMillis(), o2.toMillis()) in your Comparator.
Other solutions:
if you use Java 8, use the new date/time API;
if you use Java 7- and threetenbp, use the same methods, backported;
if you use Java 7- and Joda Time, use Period. It is nearly equivalent to that of Java 8's/threetenbp's.
If you really want to do it that way, assign your enums values like so:
DAY(1), WEEK(7), MONTH(31), YEAR(365)
than multiply in your comperator
Unit.getValue() * value
e.g. 4 MONTH gives you 31 * 4 = 124 is bigger than 100 DAY.
still you have the problem that teher are months with less than 31 days. recommend to convert to some common date units e.g. joda time.
Please use the compareto function as below:
public int compareTo(Object o) {
Test t1 = (Test)this;
Test t2 = (Test)o;
if(t1.getValue()*(t1.getUnit().getValue()+1) > t2.getValue()*(t2.getUnit().getValue()+1)) return 1;
if(t1.getValue()*(t1.getUnit().getValue()+1) < t2.getValue()*(t2.getUnit().getValue()+1)) return -1;
return 0;
}
and change the enum to
public static enum Unit {
DAY(1), WEEK(7), MONTH(31), YEAR(365) ;/* , DAY_TO_DAY */
private final int id;
Unit(int id) { this.id = id; }
public int getValue() { return id; }
}
Now you use the collection.sort(), and it will be sorted automatically

Correct implementation of time based storage of objects

For a given task, I should store an object while keeping track of the time of its creation at least per day.
The easiest solution I could find is to use Calendar to set day of year based on time stamp of objects creation.
Here is the object in a simple form:
public class PhysicalActivity implements Serializable {
private long mTimestamp;
private int mYear;
private int mMonthOfYear;
private int mWeekOfYear;
private int mWeekOfMonth;
private int mDayOfYear;
private int mDayOfMonth;
private int mDayOfWeek;
public PhysicalActivity(long timestamp){
mTimestamp = timestamp;
Calendar c = Calendar.getInstance();
c.setTimeInMillis(mTimestamp);
c.setTimeZone(TimeZone.getDefault());
mYear = c.get(Calendar.YEAR);
mMonthOfYear = c.get(Calendar.MONTH);
mWeekOfYear = c.get(Calendar.WEEK_OF_YEAR);
mWeekOfMonth = c.get(Calendar.WEEK_OF_MONTH);
mDayOfYear = c.get(Calendar.DAY_OF_YEAR);
mDayOfMonth = c.get(Calendar.DAY_OF_MONTH);
mDayOfWeek = c.get(Calendar.DAY_OF_WEEK);
}
//Getters for all members...
}
Then I have this object that holds all instances in an List<PhysicalActivity>:
public class PhysicalActivityHolder implements Serializable {
private long mCreationTimestamp;
private long mLastUpdateTimestamp;
private List<PhysicalActivity> mList;
public PhysicalActivityHolder(long creationTimestamp) {
mCreationTimestamp = creationTimestamp;
if(mList == null) {
mList = new ArrayList<PhysicalActivity>();
}
}
public void addPhysicalActivity(PhysicalActivity pa){
if(mList == null) {
throw new NullPointerException("List is null");
} else {
mList.add(pa);
}
}
public List<PhysicalActivity> getPhysicalActivitiesForDayOfYear(int dayOfYear) {
List<PhysicalActivity> activities = null;
for(PhysicalActivity p : mList) {
if(p.getDayOfYear() == dayOfYear){
activities.add(p);
}
}
return activities;
}
//Some similar methods for getting list by month of year, week of year...etc...
}
Then whenever there is a new PhysicalActivity instance is coming, I read the PhysicalACtivityHolder from disk (deserialize), add the new instance to the List<> and then save (serialize) the Holder object again to the disk.
I hope that you got my point by now. I am afraid this is not going to be a good solution. one of the problems is what if the device's timestamps changes? then the whole idea will be ruined.
I would love to know how would you implement such task? what could be the better way of doing this?
First of all, while not related to the timestamp issue, an SQLite Database would seem like a much more appropriate medium to store this information. In particular:
You don't need to serialize and deserialize the whole collection every time.
You don't need to keep the whole collection in memory.
You can add, remove, or update single events.
You can easily query by year, month, &c using SQL SELECT statements.
About the "timestamp changing" part (unless you have an external, trusted data source for the time, such as a server), yes, the device's date or time may be manually changed. However, how big of a problem would that be? This data wouldn't seem to be critically dependent on having the exact time.

calculating with data from enums

I'm trying to write a getter which takes specific data from an enum and returns a double, which I will use later in my code and I'm not sure how to go about it. This is what I've come up with so far:
//This getter takes the enum of Month and converts it so it returns the mean precipitation of a certain month
public double getPrecipitationMonth(Month month){
//more in here
return this.precipitationMonths[month.ordinal()];
The enum in question is the months of the year ie {JANUARY, FEBRUARY...} and the data for each month is in a separate file.
I'm new to programming - hope you can help! Thank you
I think your general idea is very sound. But I'd just use a Map like so:
Map<Month,Double> data = new HashMap<>;
for(Row row : readFromFile()){
data.put(row.getMonth(), row.getData());
}
So that it's more obvious.
You can use a HashMap for this, assigning enum values as keys
enum Month {
// Your enum
}
HashMap< Month, Double > monthPrecipitation = new HashMap< Month, Integer >();
public double getPrecipitationMonth( Month month ) {
return monthPrecipitation.get( month );
}
I would write eum something like that:
public enum Month {
JAN (1.0),
FEB (2.0),
MAR (3.0);
private double mId;
public static Month fromDoubleToEnum( double value ) throws Exception{
for ( Month c : Month.values() ) {
if ( c.mId == value ) {
return c;
}
}
throw new Exception( "Illegal Month value: " + value );
}
public double fromEnumToDouble(){
return mId;
}
private EModule (double num){
mId = num;
}
}
Here we have method: fromEnumToDouble that returns double on enum.
Test
public static void main(String[] args) {
Month mod = Month.FEB;
double toDouble = mod.fromEnumToDouble();
System.out.println(toDouble); // out 2.0
}
Use an EnumMap to store your precipitationMonths array instead.
EnumMap<Month,Something> precipitationMonths = new EnumMap<Month,Something>(Month.class);
precipitationMonths.put(Month.JANUARY, someValue); // Add a value
Something someValue = precipitationMonths.get(Month.JANUARY); // Get a value
Regarding that your data are located in file you need to create a class that will store that data and be able to map them.
The Java has really poor support for calendar, but you can work with it using Calendar class
You can create a enum for calendar but it is not mandatory.
enum Month{
JANUARY(Calender.JANUARY);
private final int month;
Month(int month) {
this.month = month;
}
public int month() {
return month;
}
}
When you solve the first problem of matching the data from file with months.
private final Map<Month,Double> precipitationMap = new EnumMap<>();
private void assignPrecipitation(Month month, double precipitation) {
this.precipitationMap.put(month,precipitation);
}
public double getPrecipitation(Month month) {
if(this.precipitationMap.contains(month) {
return this.precipitationMap.get(month).doubleValue();
}
throw new IllegalStateException("The Precipitation was not found for: " + myCalendar);
}

Sorting Array in Java to descending order

I'm playing around with arrays and enums and i was wondering whats the most effect way to create a Comparator class to sort these dates in descending order. Heres my code.
public enum Month {JAN(1), FEB(2), MAR(3), APR(4), MAY(5), JUN(6), JUL(7),
AUG(8), SEPT(9), OCT(10), NOV(11), DEC(12);
final int monthBoundary;
Month(int y){
monthBoundary=y;}
}
public enum Date {FIRST(1), SECOND(2), THIRD(3), FORTH(4),
FIFTH(5)... THIRTYFIRST(31);
final int dateBoundary;
Date(int z){
dateBoundary=z;}
}
//constructor etc here
private static final List<Cal> calendar = new ArrayList<Cal>();
static {
for (Month month : Month.values()) {
for (Date date : Date.values()) {
calendar.add(new Cal(date, month));
}
}
}
//creates new calendar dates
public static ArrayList<Cal> newCal() {
return new ArrayList<Cal>(calendar);
}
Using the following statement i can print the array in the order its created.
System.out.print(Card.calendar());
How do you create a Comparator class to sort these dates in descending order?
Ideally i would like it to sort the array whether it was already in order or in a random order.
At this stage i am not concerned about dates that do not exist (e.g. Feb 31st) as i'm merely practising and self studying... Just trying to get the concept :)
Thanks.
ADDED:
public ArrayList<Cal> sortDescending(ArrayList<Cal> calList){
Comparator<Cal> c = Collections.reverseOrder();
Collections.sort(calList, c);
return calList;
}
Collections.sort(list, new Comparator<Cal>() {
#Override
public int compare(Cal cal1, Cal cal2) {
int result = -cal1.getMonth().compareTo(cal2.getMonth()));
if (result == 0) {
result = -cal1.getDate().compareTo(cal2.getDate()));
}
return result;
}
});

Should I be trying to create a reversible enum in Java or is there a better way?

I seem to have faced this problem many times and I wanted to ask the community whether I am just barking up the wrong tree. Basically my question can be distilled down to this: if I have an enum (in Java) for which the values are important, should I be using an enum at all or is there a better way, and if I do use an enum then what is the best way to reverse the lookup?
Here's an example. Suppose I want to create a bean representing a specific month and year. I might create something like the following:
public interface MonthAndYear {
Month getMonth();
void setMonth(Month month);
int getYear();
void setYear(int year);
}
Here I'm storing my month as a separate class called Month, so that it is type-safe. If I just put int, then anyone could pass in 13 or 5,643 or -100 as a number, and there would be no way to check for that at compile-time. I'm restricting them to put a month which I'll implement as an enum:
public enum Month {
JANUARY,
FEBRUARY,
MARCH,
APRIL,
MAY,
JUNE,
JULY,
AUGUST,
SEPTEMBER,
OCTOBER,
NOVEMBER,
DECEMBER;
}
Now suppose that I have some backend database I want to write to, which only accepts the integer form. Well the standard way to do this seems to be:
public enum Month {
JANUARY(1),
FEBRUARY(2),
MARCH(3),
APRIL(4),
MAY(5),
JUNE(6),
JULY(7),
AUGUST(8),
SEPTEMBER(9),
OCTOBER(10),
NOVEMBER(11),
DECEMBER(12);
private int monthNum;
public Month(int monthNum) {
this.monthNum = monthNum;
}
public getMonthNum() {
return monthNum;
}
}
Fairly straightforward, but what happens if I want to read these values from the database as well as writing them? I could just implement a static function using a case statement within the enum that takes an int and returns the respective Month object. But this means if I changed anything, then I would have to change this function as well as the constructor arguments - change in two places. So here's what I've been doing. First off I created a reversible map class as follows:
public class ReversibleHashMap<K,V> extends java.util.HashMap<K,V> {
private java.util.HashMap<V,K> reverseMap;
public ReversibleHashMap() {
super();
reverseMap = new java.util.HashMap<V,K>();
}
#Override
public V put(K k, V v) {
reverseMap.put(v, k);
return super.put(k,v);
}
public K reverseGet(V v) {
return reverseMap.get(v);
}
}
Then I implemented this within my enum instead of the constructor method:
public enum Month {
JANUARY,
FEBRUARY,
MARCH,
APRIL,
MAY,
JUNE,
JULY,
AUGUST,
SEPTEMBER,
OCTOBER,
NOVEMBER,
DECEMBER;
private static ReversibleHashMap<java.lang.Integer,Month> monthNumMap;
static {
monthNumMap = new ReversibleHashMap<java.lang.Integer,Month>();
monthNumMap.put(new java.lang.Integer(1),JANUARY);
monthNumMap.put(new java.lang.Integer(2),FEBRUARY);
monthNumMap.put(new java.lang.Integer(3),MARCH);
monthNumMap.put(new java.lang.Integer(4),APRIL);
monthNumMap.put(new java.lang.Integer(5),MAY);
monthNumMap.put(new java.lang.Integer(6),JUNE);
monthNumMap.put(new java.lang.Integer(7),JULY);
monthNumMap.put(new java.lang.Integer(8),AUGUST);
monthNumMap.put(new java.lang.Integer(9),SEPTEMBER);
monthNumMap.put(new java.lang.Integer(10),OCTOBER);
monthNumMap.put(new java.lang.Integer(11),NOVEMBER);
monthNumMap.put(new java.lang.Integer(12),DECEMBER);
}
public int getMonthNum() {
return monthNumMap.reverseGet(this);
}
public static Month fromInt(int monthNum) {
return monthNumMap.get(new java.lang.Integer(monthNum));
}
}
Now this does everything I want it to, but it still looks wrong. People have suggested to me "if the enumeration has a meaningful internal value, you should be using constants instead". However, I don't know how that approach would give me the type-safety I am looking for. The way I've developed does seem overly complicated though. Is there some standard way to do this kind of thing?
PS: I know that the likelihood of the government adding a new month is...fairly unlikely, but think of the bigger picture - there are plenty of uses for enums.
This is a very common pattern, and it's fine for enums... but it can be implemented more simply. There's no need for a "reversible map" - the version which takes the month number in the constructor is better for going from Month to int. But going the other way isn't too hard either:
public enum Month {
JANUARY(1),
FEBRUARY(2),
MARCH(3),
APRIL(4),
MAY(5),
JUNE(6),
JULY(7),
AUGUST(8),
SEPTEMBER(9),
OCTOBER(10),
NOVEMBER(11),
DECEMBER(12);
private static final Map<Integer, Month> numberToMonthMap;
private final int monthNum;
static {
numberToMonthMap = new HashMap<Integer, Month>();
for (Month month : EnumSet.allOf(Month.class)) {
numberToMonthMap.put(month.getMonthNum(), month);
}
}
private Month(int monthNum) {
this.monthNum = monthNum;
}
public int getMonthNum() {
return monthNum;
}
public static Month fromMonthNum(int value) {
Month ret = numberToMonthMap.get(value);
if (ret == null) {
throw new IllegalArgumentException(); // Or just return null
}
return ret;
}
}
In the specific case of numbers which you know will go from 1 to N, you could simply use an array - either taking Month.values()[value - 1] or caching the return value of Month.values() to prevent creating a new array on every call. (And as cletus says, getMonthNum could just return ordinal() + 1.)
However, it's worth being aware of the above pattern in the more general case where the values may be out of order, or sparsely distributed.
It's important to note that the static initializer is executed after all the enum values are created. It would be nice to just write
numberToMonthMap.put(monthNum, this);
in the constructor and add a static variable initializer for numberToMonthMap, but that doesn't work - you'd get a NullReferenceException immediately, because you'd be trying to put the value into a map which didn't exist yet :(
There's a way easier way of doing this. Every enum has an ordinal() method return it's number (starting from zero).
public enum Month {
JANUARY,
FEBRUARY,
MARCH,
APRIL,
MAY,
JUNE,
JULY,
AUGUST,
SEPTEMBER,
OCTOBER,
NOVEMBER,
DECEMBER;
public Month previous() {
int prev = ordinal() - 1;
if (prev < 0) {
prev += values().length;
}
return values()[prev];
}
public Month next() {
int next = ordinal() + 1;
if (next >= values().length) {
next = 0;
}
return values()[next];
}
}
As for how to store this in a database, it depends on what persistence framework (if any) you're using. JPA/Hibernate have the option of mapping enum values by either number (ordinal) or name. Months are something you can probably take as non-changing so just use the ordinal. To get a specific value:
Month.values()[ordinalNumber];
I'm probably far behind the pack in an answer here but I tend to implement it a little bit simpler. Don't forget that 'Enum' has a values() method.
public static Month parse(int num)
{
for(Month value : values())
{
if (value.monthNum == num)
{
return value;
}
}
return null; //or throw exception if you're of that mindset
}
You shouldn't use ordinal() for this kind of thing, for the sample with months it would work (because it will not be extended) but one of the good things with enums in java is that they are designed to be possible to extend without breaking things. If you start relying on ordinal() things will break if you add some value in the middle.
I would do it like Jon Skeet suggests (he wrote it while I was writing this) but for cases where the internal number representation is in a well defined range of say 0 to 20 (or something) I would probably not use a HashMap and introduce autoboxing of the int but rather use an ordinary array (like Month[12]) but both are fine (Jon later changed his post to include this suggestion).
Edit: For the few enums where there is a natural order (like sorted months) ordinal() is probably safe to use. The problems you risk running into if you persist it will appear for things where someone might change the order of the enum. Like if:
the "enum { MALE, FEMALE }" becomes an "enum {UNKNOWN, FEMALE, MALE}" when someone extends the program in the future not knowing that you rely on ordinal.
Giving Jon a +1 for writing the same I was just writing.

Categories

Resources