When I have fetched objects from firebase realtime database into ArrayList then they are not sorted properly using Collections.sort() method. I have total 17 objects(posts) in my fetched ArrayList in which 2 objects are 1 month ago and rest 15 are under a month. Collection sorts properly these 15 objects but these 2 objects are added before 15 objects in sorting.
I am sorting these objects into ascending order of timestamp to show newer posts first and older at last. But very old posts (more than 1 month) are added before on sorting.
I have following PostModel class structure for Firebase
PostModel Class:
public class PostModel {
public String title,description;
public long timestamp;
public PostModel(){}
public PostModel(String title, String description,long timestamp) {
this.title = title;
this.description = description;
this.timestamp = timestamp;
}
}
I have passed timestamp into above class objects for insertion as System.currentTimeMillis(); and sorted using following way.
Collections.sort(postsObjs, new Comparator<PostModel>() {
#Override
public int compare(PostModel o1, PostModel o2) {
return (int) (o2.timestamp - o1.timestamp);
}
});
Help me to sort my objects properly. Thanks
Edit: I have 17 objects in my ArrayList instead of 7 and rest is same
A compare method should return -1,0 or 1;
0: if (x==y)
-1: if (x < y)
1: if (x > y)
try this:
return Long.compare(o2.timestamp - o1.timestamp);
Related
I have a structure which contains consecutive time periods (without overlap) and a certain value.
class Record {
private TimeWindow timeWindow;
private String value;
}
interface TimeWindow {
LocalDate getBeginDate();
LocalDate getEndDate(); //Can be null
}
My goal is to implement a function which takes a date and figures out the value.
A naive implementation could be to loop through all records until the date matches the window.
class RecordHistory {
private List<Record> history;
public String getValueForDate(LocalDate date) {
for (Record record : history) {
if (record.dateMatchesWindow(date)){
return record.getValue();
}
}
return null; //or something similar
}
}
class Record {
private TimeWindow timeWindow;
private String value;
public boolean dateMatchesWindow(LocalDate subject) {
return !subject.isBefore(timeWindow.getBeginDate()) && (timeWindow.getEndDate() == null || !subject.isAfter(timeWindow.getEndDate()));
}
public String getValue(){
return value;
}
}
The origin of these values are from database queries (no chance to change the structure of the tables). The list of Records could be small or huge, and the dates vary from the start of the history until the end. However, the same date will not be calculated twice for the same RecordHistory. There will be multiple RecordHistory objects, the values represent different attributes.
Is there an efficient way to search this structure?
You can use binary search to get the matching Record (if such a record exists) in O(logn) time.
Java already has data structure that do that for you, e.g. the TreeMap. You can map every Record to its starting time, then get the floorEntry for a given time, and see whether it's a match.
// create map (done only once, of course)
TreeMap<LocalDate, Record> records = new TreeMap<>();
for (Record r : recordList) {
records.put(r.getTimeWindow().getBeginDate(), r);
}
// find record for a given date
public String getValueForDate(LocalDate date) {
Record floor = records.floorEntry(date).getValue();
if (floor.dateMatchesWindow(date)) {
return r;
}
return null;
}
If the entries are non-overlapping, and if the floor entry is not a match, than no other entry will be.
PriorityQueue<StoreEmail> emails = new PriorityQueue<StoreEmail> (n,
new Comparator<StoreEmail> () {
public int compare(StoreEmail a, StoreEmail b) {
if(a.urgency != b.urgency){
return b.urgency - a.urgency;
}
else{
return a.timestamp - b.timestamp;
}
}
}
);
public class StoreEmail
{
String emailContent;
int urgency;
long timestamp;
StoreEmail(String emailContent, int urgency,long timestamp){
this.emailContent = emailContent;
this.urgency = urgency;
this.timestamp = timestamp;
}
}
Inserting in the queue
StoreEmail storeEmail = new StoreEmail(in.next(),in.nextInt(),System.currentTimeMillis());
emails.add(storeEmail);
For above comparator, Inserting following values in the priority queue.
store email value
store email5 4
store email4 4
store email3 4
store email2 4
store email1 4
Its giving different result in each run, it means comparator is not working properly, and not able to sort based on time stamp.
Note: Wanted to sort based on email value maintaining FIFO order.
Can somebody help me how to resolve this problem.
Thanks in advance. Wested a lot of time already.
I am trying to sort a list of Dates and it's not working.
Here is the declaration and get function in AttEnt
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "end_time")
private Date endTime;
public Date getEndTime() {
return endTime;
}
Here is the sorting code that isn't doing anything. GetAttempts() gets the list of all the attempts for called. They aren't in order, and I just want to be able to get whatever attempt has the latest endTime.
List<AttEnt> attempts = called.getAttempts();
Collections.sort(attempts, new Comparator<AttEnt>() {
#Override
public int compare(AttEnt a1, AttEnt a2) {
if (a1.getEndTime() == null || a2.getEndTime() == null)
return 0;
return a1.getEndTime().compareTo(a2.getEndTime());
}
});
I believe that the code above should sort attempts, and then after it attempts should be sorted, so the latest end time would be attempts.get(attempts.size()-1).getEndTime()
Comparator<AttEnt> comparator = Comparator.comparing(AttEnt::getEndTime).reversed();
attempts.sort(comparator);
Java static methods in interfaces are your friend
Click HERE for more awesomeness
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
I have an ArrayList of object called Course and I'm trying to sort it in 2 ways, by courseID and courseStartTime.
Edit: to clarify I mean I want to sort it by courseID at some point in time, and at another time later sort it by courseStartTime.
class Course implements Comparable<Course> {
private int courseID;
private String courseBeginTime;
#Override
public int compareTo(Course course) {
//what to return?
}
If I wrote 2 of my own comparators, one to compare courseID and the other for courseStarTime, then the compareTo() method in the class isn't used and I don't know what to return.
If I want to use the compareTo() method, I'm not sure how to write it so I can compare courseID and courseStartTime.
You can implement two different comparators.
public class CourseComparatorById implements Comparator<Course> {
#Override
public int compare(Course o1, Course o2) {
// for example - sort ascending by ID
return o1.getId() - o2.getId();
}
}
public class CourseComparatorByStartTime implements Comparator<Course> {
#Override
public int compare(Course o1, Course o2) {
// for example - sort ascending by start time
return o1.getStartTime() - o2.getStartTime();
}
}
And then use them to sort the array.
List<Course> courses = ...
Collections.sort(courses, new CourseComparatorById());
// now it's sorted by ID
Collections.sort(courses, new CourseComparatorByStartTime());
// now it's sorted by start time
You can also try the Java 8 Lambda way:
// this sorts by courseID
courseList.sort((c1, c2) -> Integer.valueOf(c1.courseID).compareTo(c2.courseID));
// this sorts by String courseBeginTime
courseList.sort((c1, c2) -> c1.courseBeginTime.compareTo(c2.courseBeginTime));
Note that is Java 8 you don't have to use Collections.sort, because the new List interface also provides a sort method
I have a feeling that this is being used for an online registration web app ...
you will probably be fetching the data source from a RDB ... It wouldnt be wise to put ALL courses in one list (one entity) and save that. I would create an object (containing courseID and courseBeginTime) for EVERY course and save them all. Then when querying, add hints to sort your entities based on whatever root parameters you have in them (like courseID or courseBeginTime), ending with a List containing objects sorted the way you want :) :)
May be you should do something like this
public class Course implements Comparator<Course> {
private int compareTime(int lhsTime, int rhsTime) {
if (lhsTime > rhsTime) {
return 1;
} else if (lhsTime == rhsTime) {
return 0;
} else {
return -1;
}
}
#Override
public int compare(Course lhs, Course rhs) {
if (lhs.id > rhs.id) {
return 1;
//Get the time object from course obj and pass to compaerTime
} else if (lhs.courseStarTime == rhs.courseStarTime) {
return compareTime(lhs, rhs);
} else {
return -1;
}
}
}