Java date comparator issue - java

I'm having issues sorting out my comparator by dates. The date format should be in "dd/MM/yyyy" So I call in my information from an SQL Database and then convert strings to date by doing this:
public void setDeadlineDate(String deadDate) throws ParseException {
this.d_date = deadDate;
//convert strings to dates
formatter = new SimpleDateFormat("dd/MM/yyyy");
convertedDeadlineDate = (Date)formatter.parse(deadDate);
}
I then make a get method below to call for my comparator. I have to examples but there is always a discrepancy in regards to an odd date being out of place and the comparing not being right.
Example 1:
#Override
public int compare(JobRequest j1, JobRequest j2) {
if (j1.getConvertedDeadlineDate().before(j2.getConvertedDeadlineDate())) {
return -1;
} else if (j1.getConvertedDeadlineDate().after(j2.getConvertedDeadlineDate())) {
return 1;
}
else {
return 0;
}
}
Example 2:
public int compare(JobRequest j1, JobRequest j2){
return j1.getConvertedDeadlineDate().compareTo(j2.getConvertedDeadlineDate());
}
both these examples give me issues and my priorityqueue deadlinedate not to be in the correct order as I would like.
in my db they are saved as a varchar in the following format "01/12/2012" , 1st december 2012 as it wouldnt let me use their date function to have it in the english format.
is their a better way for me to convert strings and then compare or am i missing something?
thanks
EDIT:
output getting for ordered dates:
05/04/2011
16/12/2012
18/06/2012
17/12/2013
17/12/2013
16/12/2013
17/12/2013
14/08/2012
19/12/2013
Where I declare PriortyQueue:
private Comparator<JobRequest> comparator = new JobQueueComparator(); //calls my comparator
private PriorityQueue< JobRequest> scheduledJobs = new PriorityQueue<JobRequest>(100, comparator);
public void addJob(JobRequest job) {
// now add job to priority queue
scheduledJobs.add(job); // add jobs from the resultset into queue
}
scheduledJobs.add(job) just populates the queue from a resultset and keeps adding to queue until all fields in database have been read, see below
public void populateQueueFromDB() {
// create priority queue
try {
String url = "jdbc:mysql://localhost:3306/project";
Connection conn = DriverManager.getConnection(url, "root", "nbuser");
PreparedStatement stmt = conn.prepareStatement("SELECT user_id,s_date,e_date,d_date,department,projectname,projectapplication,priority,cores,disk_space,analysis FROM booking");
ResultSet rs;
rs = stmt.executeQuery();
//List<JobRequest> jobList = new ArrayList<JobRequest>();
while (rs.next()) {
JobRequest job = new JobRequest();
User user = new User();
user.setUserID(rs.getString("user_id"));
job.setUserID(user.getUserID()); // changes the /user id to the job.setuser id so can call for my queue print.
job.setStartDate(rs.getString("s_date"));
job.setEndDate(rs.getString("e_date"));
job.setDeadlineDate(rs.getString("d_date"));
job.setDepartment(rs.getString("department"));
job.setProjectName(rs.getString("projectname"));
job.setProjectApplication(rs.getString("projectapplication"));
job.setPriority(rs.getInt("priority"));
job.setCores(rs.getInt("cores"));
job.setDiskSpace(rs.getInt("disk_space"));
job.setAnalysis(rs.getString("analysis"));
schedulerPriorityQueue.addJob( job );
}
schedulerPriorityQueue.printQueue();
conn.close();
} catch (Exception e) {
System.err.println("Got an exception! ");
System.err.println(e.getMessage());
}
}
}
PRINT QUEUE:
public void printQueue() {
for (JobRequest jr : scheduledJobs) {
System.out.print(jr.getUserID() + "-->");
System.out.print(jr.getStartDate() + "--START-->");
System.out.print(jr.getEndDate() + "---END-->");
System.out.print(jr.getDeadDate() + "--DROP-->");
System.out.print(jr.getDepartment() + "-->");
System.out.print(jr.getProjectName() + "-->");
System.out.print(jr.getProjectApplication() + "-->");
System.out.print(jr.getPriority() + "--PRIORITY-->");
System.out.print(jr.getCores() + "-->");
System.out.print(jr.getDiskSpace() + "-->");
System.out.println(jr.getAnaylsis());
}
}

when you do: for (JobRequest jr : scheduledJobs) { ... you're actually using an implicit Iterator, which doesn't guarantee that the items will be returned in order of Priority (defined by your comparator).
Like mentioned above, the documentation states that:
This class and its iterator implement all of the optional methods of
the Collection and Iterator interfaces. The Iterator provided in
method iterator() is not guaranteed to traverse the elements of the
priority queue in any particular order. If you need ordered traversal,
consider using Arrays.sort(pq.toArray()).
Meaning that the order of the jobs in queue and the order of the iterator isn't necessarily the same.
If you just need to list the jobs, use the Arrays.sort approach mentioned in the docs.
If you want to make sure that your PriorityQueue is working, you'll have to use it as one, doing something like:
while(!scheduledJobs.isEmpty()) {
... = scheduledJobs.poll();
//print, do whatever
}
Keep in mind that this removes the elements from the Queue, and should only be used for testing purposes, or to actually handle the jobs as you need. But this should show you the dates in the actual Priority order.
It seems to me that there's some confusion on how the PriorityQueue should be used. I would say that the regular use case would be something like:
Add elements to the queue
Define a method to handle the "jobs" (each element), in the queue
While the queue isn't empty, get each element using poll (returns the smallest), and do whatever you need to do with it.

The documentation of PriorityQueue says that
The Iterator provided in method iterator() is not guaranteed to
traverse the elements of the priority queue in any particular order.
If you need ordered traversal, consider using
Arrays.sort(pq.toArray())
.
http://docs.oracle.com/javase/6/docs/api/java/util/PriorityQueue.html

This works perfectly for me:
public class JobRequest implements Comparable<JobRequest> {
static final DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
Date convertedDeadlineDate;
Date getConvertedDeadlineDate() {
return convertedDeadlineDate;
}
public JobRequest(String deadDate) {
try {
convertedDeadlineDate = (Date) formatter.parse(deadDate);
} catch (ParseException e) {
e.printStackTrace();
}
}
#Override
public int compareTo(JobRequest j) {
return this.getConvertedDeadlineDate().compareTo(
j.getConvertedDeadlineDate());
}
public static void main(String[] args) {
List<JobRequest> list = new ArrayList<>();
list.add(new JobRequest("10/10/2010"));
list.add(new JobRequest("09/09/2009"));
list.add(new JobRequest("11/11/2011"));
list.add(new JobRequest("08/08/2008"));
Collections.sort(list);
for (JobRequest j : list) {
System.out.println(formatter.format(j.getConvertedDeadlineDate()));
}
}
}
Which results in the output:
08/08/2008
09/09/2009
10/10/2010
11/11/2011

Related

How can I use the Collections.sort method with 2 parameters in order to sort in ascending order a given list in Java?

I've created a class "Planificator" that will allow me to sort the elements of the list by the end date.
Then I would like to delete the overlapping elements.
Basically my code will help me implement a meeting system.
Two meetings with starting dates in ascending order overlap if the start date of the second meeting is less than or equal to the start date of the first meeting.
I've put in my code what would be my example of an expected output to make the things look clear.
The code works on that example but I think the way I used Collections.sort is not good because it will not work in every case. I'm getting errors if I'm trying something like this Collections.sort(meetings ,new Compara());.
class Meeting implements Comparable<Meeting> {
private Calendar start, end;
public Meeting(Calendar start, Calendar end) {
if (start.compareTo(end) > 0)
throw new IllegalArgumentException("Invalid date");
this.start = start;
this.end = end;
}
public Calendar getStarted() {
return start;
}
public Calendar getEnding() {
return end;
}
public int compareTo(Meeting m) {
return this.start.compareTo(m.getStarted());
}
public String toString() {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
return sdf.format(start.getTime()) + " -> " + sdf.format(end.getTime());
}
}
class Planificator {
public class Compara implements Comparator<Meeting> {
#Override
public int compare(Meeting o1, Meeting o2) {
return o1.getEnding().compareTo(o2.getEnding());
}
}
public static void planifica(List<Meeting> meetings) {
Collections.sort(meetings);
for (int i = 0; i < meetings.size() - 1; i++) {
if (meetings.get(i).getEnding().compareTo(meetings.get(i + 1).getStarted()) > 0) {
meetings.remove(i + 1);
--i;
}
}
}
}
public class Main {
public static void main(String[] args) throws Exception {
try {
List<Meeting> l = new ArrayList<>();
l.add(new Meeting(new GregorianCalendar(1,2,3), new GregorianCalendar(2,2,3)));
l.add(new Meeting(new GregorianCalendar(1, 2, 3), new GregorianCalendar(5, 2, 3)));
l.add(new Meeting(new GregorianCalendar(3, 2, 3), new GregorianCalendar(5, 2, 3)));
Planificator.plan(l);
System.out.println(l); // [03/03/0001 12:00:00 -> 03/03/0002 12:00:00, 03/03/0003 12:00:00 -> 03/03/0005 12:00:00]
} catch (IllegalArgumentException e) {
System.out.print(e.getMessage());
}
}
}
Your problem is that public class Compara needs to be a static class for you to be able to instantiate it in main() because otherwise the nested class expects to have an associated instance.
You can read more about nested classes and inner classes here: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
Also its much cleaner to write this simply as:
// inverted order or whatever you want
Collection.sort(meetings, (o1, o2) -> o2.getEnding().compareTo(o1.getEnding());
Lambdas make this sort of code much cleaner
Collections.sort(meetings, new Compara()),
-- in this way you can optimize your sort Algorithm, I manage to get 67 points😎...but there are some cases that I don't cover them
-Also you may want to make Abstract the Compara class

Duplicate items added in ConcurrentSkipListSet

I am trying to maintain insertion order in ConcurrentSkipListSet. The item being added is a custom class type with value(String) and index (int) properties. It implements Comparable interface. The set behaves very inconsistently, sometimes adding duplicate items. Items are considered duplicate if they have same value.
// This is the Item class being added in the set.
final class Item implements Comparable<Item> {
private String value;
private int index;
Item(String val, int idx) {
this.value = val;
this.index = idx;
}
#Override
public int compareTo(Item o) {
// returns zero when values are equal indicating it's a duplicate item.
return this.value.equals(o.value) ? 0 : this.index - o.index;
}
#Override
public String toString() {
return this.value;
}
}
// Below is the main class.
public class Test {
ConcurrentSkipListSet<Item> set;
AtomicInteger index;
public Test() {
set = new ConcurrentSkipListSet<>();
index = new AtomicInteger(0);
}
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
Test test = new Test();
test.addItems();
test.assertItems();
}
}
//trying to test it for 10 times. It always fails for once or twice.
private void assertItems() {
Iterator<Item> iterator = set.iterator();
String[] values = {"yyyy", "bbbb", "aaaa"};
for (String value : values) {
if (!value.equals(iterator.next().toString())) {
System.out.println("failed for :" + set);
return;
}
}
System.out.println("passed for :" + set);
}
//adding items with some duplicate values
private void addItems() {
set.add(new Item("yyyy", index.getAndIncrement()));
set.add(new Item("bbbb", index.getAndIncrement()));
set.add(new Item("yyyy", index.getAndIncrement()));
set.add(new Item("aaaa", index.getAndIncrement()));
}
Expected : passed for :[yyyy, bbbb, aaaa]
Actual : failed for :[yyyy, bbbb, yyyy, aaaa]
But as mentioned before, the result is very inconsistent. Most of the times, it passes.
Please let know what could be the reason for this behavior. Is the 'compareTo()' method wrong? If so, it should always fail.
Ideally we should override 'equals()' method also. But it doesn't matter from sorted set perspective.
Appreciate your help.
You have broken the contract of compareTo, which results in undefined behaviour.
Finally, the implementor must ensure that x.compareTo(y)==0 implies
that sgn(x.compareTo(z)) == sgn(y.compareTo(z)), for all z.
You can easily see that you fail this requirement by pulling your Items out into variables:
final Item x = new Item("yyyy", index.getAndIncrement());
final Item z = new Item("bbbb", index.getAndIncrement());
final Item y = new Item("yyyy", index.getAndIncrement());
System.out.println(x.compareTo(y));
System.out.println(x.compareTo(z));
System.out.println(y.compareTo(z));
Output:
0
-1
1
The signs are different, therefore the contract has been broken.
In your compareTo-implementation you are mixing two different properties in an illegal way. Thus you break the contract of the Comparable interface.
In your comparison, you look at the index only if the values are not equal. This way you do not define an overall natural order for your items. Depending on what comparison is done first, the result of sorting a list will be random.
#Override
public int compareTo(Item o) {
int vCompare = this.value.compareTo(o.value);
if (vCompare == 0) {
return this.index - o.index;
}
return vCompare;
}
This implementation will first compare by value and then by index. It adheres to the Comparable contract and actually defines a natural order for Items and works fine with the Set implementation.
Caution: This sample implementation will break the tests.
The tests are there to show the code behaves as intended. But in this case the intended behavior is the actual issue.
It is incompatible with the Comparable contract.
You cannot sort a list by numeric index and expect a lookup by alphabetical value to succeed. But that's exactly what is attempted here. Sort by index but find duplicate names. It does not work this way.
I don't know the implementation of ConcurrentSkipListSet in detail, but it looks like you need to override the equals method of your class to specify what qualifies two objects to be equal.
This is not an answer, rather a solution to achieve the objective based on root cause finding by #Michael and #Jochen. Modified the Item class comparator to below to have natural order of value String.
public int compareTo(Item o) {
return this.value.compareTo(o.value);
}
And then, added an index based comparator to achieve FIFO retrieval.
// This iterator would now be used in assertItems() method in main class.
private Iterator<Item> getFIFOIterator() {
ArrayList<Item> list = new ArrayList<>(set);
list.sort(Comparator.comparingInt(Item::getIndex));
return list.iterator();
}
#Michael and #Jochen : Appreciate you for taking your time and figuring out the root cause.

Looping over a huge list, check string equal to true

I have a requirement as below:
List<User> userList = listOfUsers(); // Morethan 50,000 users
I need to find a user status from the list of users. if any one of the users is active then break the loop.
what is the efficient way to handle this in java ?
Java 8 solution with method reference:
userList.stream().filter(User::isActive).findFirst()
It'll return Optional so you could map over it.
One way to accelerate the search (Without Using Java 8) is by searching both directions in the ArrayList (i.e from the beginning to the middle, and from the end to the middle) at the same time via using multi-threading, I created this example and tested it against 1 million object/user to check if any of them is active (Note that I made only one user active and put him in the middle to see the longest time the search may take).
import java.util.ArrayList;
public class User {
// some fields to test
String name;
boolean active;
//volatile means all writes up to the volatile variable
//from other any thread are now visible to all other threads.
//so they can share working on that variable
static volatile boolean finishFirst = false; // to announce first thread finish
static volatile boolean finishSecond = false; // to announce second thread finish
static volatile boolean found = false; // // to announce if an active user found
/**
* Simple Constructor
* #param name
* #param active
*/
public User(String name, boolean active){
this.name = name;
this.active = active;
}
public static void main(String[] args) {
// create an ArrayList of type User
ArrayList<User> list = new ArrayList<User>();
// populate it with 1 MILLION user!!
int i=0;
for(;i<1000000; i++){
// make only the one in the very middle active to prolong the search to max
if(i==500000){
list.add(new User(String.valueOf(i),true));
}
else{
list.add(new User(String.valueOf(i),false));
}
}
System.out.println("End of Adding " + i + " User" );
// to measure how long it will take
long startTime, endTime;
startTime = System.currentTimeMillis();
System.out.println("Found Any Active: "+ isAnyActive(list)); // invoke the method
endTime = System.currentTimeMillis();
System.out.println(endTime-startTime + " MilliScond");
}
public static boolean isAnyActive(ArrayList<User> list){
found = false;
// create two threads, each search the half of the array
// so that shall save time to half
Thread t1 = new Thread(new Runnable(){
#Override
public void run() {
// read one more index in case the size is not an even number
// so it will exceed the middle in one -> no problem at all
for(int i=0; i<=(list.size()/2)+1; i++){
if(list.get(i).active) {
found = true;
finishFirst = true;
break;
}
}
finishFirst = true; // in case did not find any
}
});
// second thread the same, but read from the end to the middle
Thread t2 = new Thread(new Runnable(){
public void run() {
for(int i=list.size()-1; i>=list.size()/2; i--){
if(list.get(i).active) {
found = true;
finishSecond = true;
break;
}
}
finishSecond = true;
}
});
// start both thread
t2.start();
t1.start();
// while one of them has not finished yet
while(!finishFirst || !finishSecond){
// but in case not finished looping but found an active user
// break the loop
if(found){break;}
}
return found; // return the result
}
}
Test
End of Adding 1000000 User
Found Any Active: true
31 MilliScond
The efficient way is to do that filter with SQL if you are using that. Select just the active users....
When you have all that list to work with java it will be slow as hell and there is no magic here, you will need to iterate.
public User getActiveUserFromList(userList) {
for (User user : userList) {
if (user.isActive()) {
return user;
}
return null;
}
}
If you have that list anyway ordered you can try to hack it, let's assume it is ordered by active status
public Boolean isAnyActive(userList) {
if (userList.first().isActive()) { // try first
return true;
}
if (userList.last().isActive()) { // if its ordered and there is an active user, the last surely will be active, since first wasn't
return true;
}
return false;
}
I would certainly think about using Java 8 Lambda. I have written an example class:
package com.chocksaway;
import java.util.ArrayList;
import java.util.List;
/**
* Author milesd on 05/06/2017.
*/
class Name {
private String name;
private Boolean status;
public Name(String name, Boolean status) {
this.name = name;
this.status = status;
}
public String getName() {
return name;
}
public Boolean getStatus() {
return status;
}
}
public class FindFirstInStream {
public static void main(String[] args) {
List<Name> userList = new ArrayList<>();
userList.add(new Name("James", false));
userList.add(new Name("Eric", true));
userList.add(new Name("David", false));
Name firstActiveName = userList.stream()
.filter(e -> e.getStatus().equals(true))
.findFirst()
.get();
System.out.println(firstActiveName.getName());
}
}
I've created a Name class, with name, and status.
I populate a userList with James, Eric, and David.
I use Java 8 stream to filter, and return the first "active" name (Eric).
This is stored in "firstActiveName".
You may use Collections ArrayDeque. ArrayDeques will use half of the iteration to find the active user. In your case
ArrayDeque sample = new ArrayDeque(userList);
for(int i=0;i<sample.size();i++){
if(sample.pollFirst().status.equalsIgnoreCase("A")) {
break;
}
if(sample.pollLast().status.equalsIgnoreCase("A")) {
break;
}
if(sample.size()==0) break;
}
Because I see many Java 8 streaming solutions that do not use parallel streams, I add this answer. You have a large collection on which you do the matching, so you can use the power of parallelStreams when you would opt to use Java 8.
Optional<User> result = userList.parallelStream().filter(User::isActive).findAny();
Using a parallelStream will split the stream into multiple sub-streams which is more performant for very large collections. It uses the ForkJoinPool internally to process these sub-streams. The only difference here is that I use findAny() instead of findFirst() in this solution.
This is what Javadoc has to say about findAny():
The behavior of this operation is explicitly nondeterministic; it is
free to select any element in the stream. This is to allow for maximal
performance in parallel operations; the cost is that multiple
invocations on the same source may not return the same result. (If a
stable result is desired, use findFirst() instead.)
Here is a nice tutorial on Parallelism from Oracle.

How to remove if else condition from loop?

I have a code snippet similar to the one below,
public ArrayList getReport(reportJDOList,accountType)
{
String abc = "";
for(ReportJDO reportJDO : reportJDOList)
{
if(accountType.equals("something")
abc = reportJDO.getThis();
else
abc = reportJDO.getThat();
//somecode goes here
}
returning List;
}
As I know the value of accountType before the iteration, I dont want this check to happen, for every entry in a list as it would cause numerous number of checks if the size of reportJDOList is 10000 for an instance. How we remove this thing from happening? Thanks in Advance :)
You can indeed peform check once and implement 2 loops:
if(accountType.equals("something") {
for(ReportJDO reportJDO : reportJDOList) {
abc = reportJDO.getThis();
}
} else {
for(ReportJDO reportJDO : reportJDOList) {
abc = reportJDO.getThat();
}
}
Obviously you can improve your design by either
separating you loops into 2 different methods
Using command pattern, i.e. implementing loop body in different command and executing it to loop.
Using Guava's Function (it is just improvement of #2)
Using java 8 streams.
IF you want to save the String comparison, make it once before the loop and store the result in a boolean variable :
String abc = "";
boolean isThis = accountType.equals("something");
for(ReportJDO reportJDO : reportJDOList) {
abc = isThis ? reportJDO.getThis() : reportJDO.getThat();
//somecode goes here
}
I'd vote for clean coding this - perform the check once and delegate the logic into private methods, each performing the loop individually. This duplicates code for the loop but gives greatest flexibility if at some point you need to do something more in SomethingReport that's not duplicated in OtherReport.
public ArrayList getReport(reportJDOList,accountType) {
if("soemthing".equals(accountType)) {
return getSomethingReport(reportJDOList);
} else {
return getOtherReport(reportJDOList);
}
}
private ArrayList getSomethingReport(reportJDOList) {
[...]
}
interface AccountHandler {
String get(Report r);
}
AccountHandler thisHandler= new AccountHandler() {
#Override
public String get(Report r) {
return r.getThis();
}
};
AccountHandler thatHandler= new AccountHandler() {
#Override
public String get(Report r) {
return r.getThat();
}
};
//...............
AccountHandler ah;
ah = (what.equalsIgnoreCase("this")) ? thisHandler : thatHandler;
Report r=new Report();
// loop
ah.get(r);
//Using reflection:
Report r = new Report();
Method thisMethod = r.getClass().getDeclaredMethod("getThis");
Method thatMethod = r.getClass().getDeclaredMethod("getThat");
Method m = (what.equalsIgnoreCase("this")) ? thisMethod : thatMethod;
m.invoke(r);

java : mapping the Values from Enumeration to Object

I have a Enumeration as shown in below program
public class Test {
public static void main(String args[]) {
Vector v = new Vector();
v.add("Three");
v.add("Four");
v.add("One");
v.add("Two");
Enumeration e = v.elements();
load(e) ; // **Passing the Enumeration .**
}
}
There is also a Student Object
public Student
{
String one ;
String two ;
String three ;
String four ;
}
i need to pass this Enumeration to another method as shown below
private Data load(Enumeration rs)
{
Student stud = new Student();
while(rs.hasMoreElements())
{
// Is it possible to set the Values for the Student Object with appropiate values I mean as shown below
stud.one = One Value of Vector here
stud.two = Two Value of Vector here
stud.three = Three Value of Vector here
stud.four = Four Value of Vector here
}
}
Please share your ideas on this .
Thanks
Sure. You could use the elementAt method, documented here, to get the value you wanted. Do you have a specific reason you are using a Vector? Some of the List implementations might be better.
Enumerations don't have the idea of "first value", "second value", etc. They just have the current value. You could work around this in various ways:
The easy way -- convert it to something easier to work with, like to a List.
List<String> inputs = Collections.list(rs);
stud.one = inputs.get(0);
stud.two = inputs.get(1);
// etc.
Keep track of the position yourself.
for(int i = 0; i <= 4 && rs.hasNext(); ++i) {
// Could use a switch statement here
if(i == 0) {
stud.one = rs.nextElement();
} else if(i == 1) {
stud.two = rs.nextElement();
} else {
// etc.
}
}
I really don't recommend either of these things, for the following reasons:
If you want your parameters in a particular order, just pass them in that way. It's much easier and it's also easier to maintain (and for other people to read).
void example(String one, String two, String three, String four) {
Student student = new Student();
student.one = one;
student.two = two;
// etc.
}
You shouldn't use Enumeration at all, since it's been replaced with Iterator and Iterable since Java 1.2. See ArrayList and Collection.

Categories