sort class by several date-fields - java

I have a class "entry" which has several date fields:
creationDate (always available)
manualSetDate (not always)
meetingDate (not always)
Now I'd like to sort the entries by first meetingDate (to have all entries of a meeting together), then or if not available at one entry by manualSetDate, and if they have the same meeting or if one entry has no manualSetDate also by the creationDate.
The difficulty is that many entries don't have a meeting- or a manualSetDate. So I have iterated over all (I hope so) possible combinations like:
Date relevant1; //at the end i compare both relevant dates
Date relevant2;
if e1.creationdate != null && e2.creationdate == null)
{relevant2 = e2.creationdate}...
if e1.creationdate == null && e2.creationdate != null)
{relevant2 = e2.creationdate}...
...
Which leads to high complexity
Sort order:
Meeting Date (if both have one)
ManualSetDate (if both have one)
CreationDate
I traversed over all combinations, but it seems to make mistakes...
Is there a better way to do the sorting? any ideas?

I think I would make a function in my Entry that found the "relevant" date. Then have your compareTo use that new function. This way you aren't accounting for every possible combination as your code above does.
public class Entry implements Comparable<Entry>
{
private Date getAvailableDate()
{
if (meetingDate != null)
return meetingDate;
else if (manualSetDate != null)
return manualSetDate;
else
return creationDate;
}
public int compareTo(Entry other)
{
return this.getAvailableDate().compareTo(other.getAvailableDate());
}
}

Better to write your own Comparator and implement compare method something like below code
class MyComparator implements Comparator<DateEntry>{
#Override
public int compare(DateEntry o1, DateEntry o2) {
int i = o1.creationDate.compareTo(o2.creationDate);
if (i != 0) return i;
if(o1.manualSetDate!=null&&o2.manualSetDate!=null)
i = o1.manualSetDate.compareTo(o2.manualSetDate);
if (i != 0) return i;
if(o1.meetingDate!=null&&o2.meetingDate!=null)
i = o1.meetingDate.compareTo(o2.meetingDate);
if (i != 0) return i;
return i;
}
}
And then Sort them like.
Collections.sort(list,new MyComparator ());

I suggest that you use Comparator to define order and then some library function to do actual sorting. I would also go for multiple early returns in compare method. Implementation is something like following:
public class Entry {
...
public Date getMeetingDate() { return meetingDate;}
public Date getManualSetDate() { return manualSetDate; }
public Date getCreationDate() { return creationDate; }
}
public class EntryComparator implements Comparator<Entry> {
#Override
public int compare(Entry o1, Entry o2) {
if (o1.getMeetingDate() != null && o2.getMeetingDate() != null) {
int compared = o1.getMeetingDate().compareTo(o2.getMeetingDate());
if (compared != 0) {
return compared;
}
}
if (o1.getManualSetDate() != null && o2.getManualSetDate() != null) {
int compared = o1.getManualSetDate().compareTo(o2.getManualSetDate());
if (compared != 0) {
return compared;
}
}
return o1.getCreationDate().compareTo(o2.getCreationDate());
}
}
//and actual sorting:
List<Entry> entries ...
Collections.sort( entries, new EntryComparator() );

Related

how to reduce the time complexity of two nested loop by using Map in java

I have two List of employeeData object
List<EmployeeDatas> allEmployees
List<EmployeeDatas> currentEmployees
Class EmployeeDatas {
String name;
String lastName;
String joiningDate;
String promotionDate;
}
I want to compare the second list data with the first one without using the double nested loop
if the data matched return true otherwise false.
for (allEmployee : allEmployees) {
for ( currentEmployee : currentEmployees ) {
if(allEmployee.name.equal(currentEmployee.name) &&
allEmployee.lastName.equal(currentEmployee.lastName) &&
allEmployee.joiningDate.equal(currentEmployee.joiningDate) &&
allEmployee.promotionDate.equal(currentEmployee.promotionDate)) {
return true;
}
}
}
is it possible to use Map and solve it in O(N) time rather O(N^2)
You can use HashSet for that purpose. But for that, you need the equals/hashCode contract to be properly implemented.
Here's an example:
public class EmployeeData {
private String name;
private String lastName;
private String joiningDate;
private String promotionDate;
#Override
public boolean equals(Object o) {
return o instanceof EmployeeData other
&& Objects.equals(name, other.name)
&& Objects.equals(lastName, other.lastName)
&& Objects.equals(joiningDate, other.joiningDate)
&& Objects.equals(promotionDate, other.promotionDate);
}
#Override
public int hashCode() {
return Objects.hash(name, lastName, joiningDate, promotionDate);
}
}
Now to check if at least one employee from the currentEmployee list is present in the allEmployees you can dump the contents of allEmployees into a HashSet and iterate over currentEmployee checking the elements against the set. Such check has an amortized cost of O(1), therefore the overall time complexity would be O(n) (*n - represents the number of elements in the currentEmployee list).
public static boolean containsAny(List<EmployeeData> allEmployees,
List<EmployeeData> currentEmployees) {
Set<EmployeeData> allEmp = new HashSet<>(allEmployees);
for (EmployeeData currentEmployee : currentEmployees) {
if (allEmp.contains(currentEmployee)) {
return true;
}
}
return false;
}
We can make this code very concise by using Stream API.
public static boolean containsAny(List<EmployeeData> allEmployees,
List<EmployeeData> currentEmployees) {
Set<EmployeeData> allEmp = new HashSet<>(allEmployees);
return currentEmployees.stream().anyMatch(allEmp::contains);
}
Note: this code behaves in exactly the same way as the snippet you're provided, i.e. it would return after the first match.
It you want to find out whether all elements from the currentEmployee list are present in the currentEmployee, then you can make use of the method Collection.containsAll():
public static boolean containsAll(List<EmployeeData> allEmployees,
List<EmployeeData> currentEmployees) {
Set<EmployeeData> allEmp = new HashSet<>(allEmployees);
return allEmp.containsAll(currentEmployees);
}

Comparing list of objects in Java 8

This post may be duplicate one, apologies for that.
I have worked extensively in Java-6, now moving to Java 8. Is there an efficient way to rewrite the below logic in Java 8?
This compares list of objects with single object, returning true if some object in the list has a matching "Identifier" parameter value.
private boolean compareOrder(UserOrderDTO.OrderConfig givenDeviceConfig, List<UserOrderDTO.OrderConfig> masterConfigList) {
boolean isValidService = false;
for(UserOrderDTO.OrderConfig eachRecord:masterConfigList) {
if(eachRecord.getIdentifier()!=null && givenDeviceConfig.getIdentifier()!=null) {
if(eachRecord.getIdentifier().trim().equalsIgnoreCase(givenDeviceConfig.getIdentifier().trim()) ) {
isValidService = true;
break;
}
}
}
return isValidService;
}
Also if I want to compare two list, any suggestions please
List<UserOrderDTO.OrderConfig> oneList = some value;
List<UserOrderDTO.OrderConfig> twoList = some value;
private boolean compareOrder(UserOrderDTO.OrderConfig givenDeviceConfig, List<UserOrderDTO.OrderConfig> masterConfigList) {
return givenDeviceConfig.getIdentifier() != null
&& masterConfigList.stream().anyMatch(
p -> p.getIdentifier() != null
&& p.getIdentifier().trim().equalsIgnoreCase(givenDeviceConfig.getIdentifier().trim()));
}
.
private static boolean compareOrderLists(List<UserOrderDTO.OrderConfig> list1, List<UserOrderDTO.OrderConfig> list2) {
return list1.stream().anyMatch(
p -> p.getIdentifier() != null
&& list2.stream().anyMatch(
p2 -> p2.getIdentifier() != null
&& p2.getIdentifier().trim().equalsIgnoreCase(p.getIdentifier())));
}
private boolean compareOrder(OrderConfig givenDeviceConfig, List<OrderConfig> masterConfigList) {
//do null and empty checks here
return masterConfigList
.stream()
.anyMatch(o -> o.getIdentifier().equalsIgnoreCase(givenDeviceConfig.getIdentifier()));
}
You can use this for list comparison. If you don't want to include a library, there are plenty of answers on stackoverflow to compare Collections.

CompareTo is transitive

I have a POJO looking like this:
public class Pojo implements Comparable<Pojo> {
private String type;
private String journalId;
private Date bookingDate;
private Long account;
private String description;
private BigDecimal debit;
private BigDecimal credit;
....
}
and I want to sort a list of these POJOs. Currently my compareTo method looks like this:
#Override
public int compareTo(EfdisJournal other) {
int i = this.type.compareTo(other.type);
if (i != 0)
return i;
if (this.bookingDate != null && other.bookingDate != null)
i = this.bookingDate.compareTo(other.bookingDate);
if (i != 0)
return i;
if (this.journalId != null && other.journalId != null)
i = this.journalId.compareTo(other.journalId);
if (i != 0)
return i;
return this.account.compareTo(other.account);
}
If I run a sort with this compareTo method, I get this java.lang.IllegalArgumentException: Comparison method violates its general contract error. I did google a bit and I think it happens because some of the fields are null on comparison. Yet I have no idea how to solve this or if I am right why that error appears.
The comparison should work like this: 1st compare by type, then compare by bookingDate, as 3rd compare by journalId and at last compare by account. All comparisons should be ascending.
type is never null
bookingDate may be null
journalId may be null
account is never null
EDIT:
Sadly I was not able to implement the method, so that the order is as needed. Yet, i solved the problem I had, because the stored procedure yielded 2 resultsets, of which the second was order as needed, so the only thing I had to do was to use the 2nd resultset instead of the first.
You need to deal with the case where one instance has a null bookingDate, and the other has a non-null bookingDate.
You should decide whether things with null bookingDate should be sorted before or after things with a non-null bookingDate, and write your compareTo appropriately. (And then journalId too.) Then you can get an order that sorts consistently.
For instance:
#Override
public int compareTo(EfdisJournal other) {
int i = this.type.compareTo(other.type);
if (i != 0) {
return i;
}
if ((this.bookingDate==null) ^ (other.bookingDate==null)) {
return (this.bookingDate==null ? -1 : 1);
}
if (this.bookingDate != null && other.bookingDate != null) {
i = this.bookingDate.compareTo(other.bookingDate);
}
if (i != 0) {
return i;
}
if ((this.journalId==null) ^ (other.journalId==null)) {
return (this.journalId==null ? -1 : 1);
}
if (this.journalId != null && other.journalId != null) {
i = this.journalId.compareTo(other.journalId);
}
if (i != 0) {
return i;
}
return this.account.compareTo(other.account);
}
You're ignoring situations where bookingDate and/or journalId is null with one and non-null with the other.

sort list of custom object contains date from latest to oldest in java

I have custom object contains Date, which I creates dynamically and filled with the data (name, date etc..) and add to the list and after adding all objects into the list I want to sort the list based on the date of custom object (From LATEST to OLDEST).
How do I achieve it in Java?
Please provide me the solution with some sample example.
Thanks.
to Find the difference between 2 dates , you can compare the values. covert dates into Value(long) and than compare rather than using Arithmetic operation like subtraction etc.
public class CompareObjects implements Comparator {
#Override
public int compare(classA c1, classA c2) {
long value1 = c1.getDate().getTime();
long value2 = c2.getDate().getTime();
if (value2 > value1) {
return 1;
} else if (value1 > value2) {
return -1;
} else {
return 0;
}
}
public static void main(String[] args) {
classA o1 = new classA();
o1.setDate(new Date());
classA o2 = new classA();
o2.setDate(new Date());
CompareObjects compare = new CompareObjects();
int i = compare.compare(o1, o2);
System.out.println(" Result : " + i);
}
}
or without converting you can directly return the result.
return c2.getDate().compareTo(c1.getDate());
after comparison you can use Collection.sort method to set the order.
You need to implement a custom Comparator or implement Comparable.
For more details see here.
http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html
http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html
Here are some examples of using either of them.
http://www.mkyong.com/java/java-object-sorting-example-comparable-and-comparator/
You could use custom Comparator and then Collections.sort(collection, yourComparatorInstance);
See
an example
With null in first position and then from the oldest to the newest :
Collections.sort(myList, new Comparator<MyBean>() {
public int compare(MyBean b1, MyBean b2) {
if (b1 == null || b1.getMyDate() == null) {
return -1;
}
if (b2 == null || b2.getMyDate() == null) {
return 1;
}
return b1.getMyDate().compareTo(b2.getMyDate());
}
});

Sorting custom data structure on Key in TreeMap

I am trying to sort a TreeMap on key. Key is some custom DataStructure having int, List, String, etc.
The member on which I am expecting a sort has some duplicates. Let's say that member is Rank. More than 1 object can have same rank.
Simplified version example:
NOTE: in the CompareTo method below 0 is not returned intentionally to NOT ignore duplicates.(Please correct me if this is not the right way to avoid duplicates)
import java.util.TreeMap;
public class TreeTest {
public static void main(String[] args) {
TreeMap<Custom,String> t = new TreeMap<Custom,String>();
Custom c1 = new Custom();
c1.setName("a");
c1.setRank(0);
Custom c2 = new Custom();
c2.setName("b");
c2.setRank(1);
Custom c3 = new Custom();
c3.setName("c");
c3.setRank(0);
t.put(c1, "first");
t.put(c2, "Second");
t.put(c3, "Third");
System.out.println(t.keySet());
for(Custom c:t.keySet()){
System.out.println(t.get(c));
}
}
}
And Custom Object
package com.example.ui;
public class Custom implements Comparable<Custom>{
int rank;
String name;
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + rank;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Custom other = (Custom) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (rank != other.rank)
return false;
return true;
}
// 0 is not returned intentionally to NOT ignore duplicates.
public int compareTo(Custom o) {
if(o.rank>this.rank)
return 1;
if(o.rank==this.rank)
return -1;
return -1;
}
}
Output::
[com.example.ui.Custom#fa0, com.example.ui.Custom#fbe, com.example.ui.Custom#f80]
null
null
null
Expected:
First, Second, Third based on Rank 0,1,0 respectively.
I looked at couple of examples on Google. Most of them were basic usage on TreeMap sort using keys or values with primitive datatypes, but none with duplicates when sorting member
is a part of custom key DataStructure.
Please help?
The problem is that your implementation of compareTo is not consistent with equals, which is required by TreeMap. From the API docs:
Note that the ordering maintained by a sorted map (whether or not an
explicit comparator is provided) must be consistent with equals if
this sorted map is to correctly implement the Map interface.
One possible consistent implementation would be to first compare by rank and then by name if the rank values are equal. For two instances of Custom with equal ranks and identical names you should not expect to be able to store them both as keys within the same Map - This violates the contract of Map.
public int compareTo(Custom o) {
int ret = this.rank - o.rank;
// Equal rank so fall back to comparing by name.
if (ret == 0) {
ret = this.name.compareTo(o.name);
}
return ret;
}
As mentioned, your implementation of equals and compareTo are not consistent with each other. If I read your question correctly, what you require is to preserve duplicates that have the same key. I'd recommend you to look into the TreeMultimap of the Google Guava collections. It creates set containers for each value object sothat different values having the same key are preserved.
e.g.
treeMultimap.put ("rank1", "Joe");
treeMultimap.put ("rank1", Jane");
treeMultimap.get ("rank1"); // Set("Joe","Jane");
The constrain in this data structure is that K,V pairs must be unique. That is, you can't insert ("rank1", "Joe") twice in the Multimap.
One important note: The reason why you see so many examples of Map, using simple types and, in particular, strings, is that keys in a map must be immutable. The equals and hashcode values of an object must not change in the time it's used as a key in a map. Translated to your example, you cannot do customObject.setRank(...) and updates a rank value when it's used as a key. To do so, you first need to remove the key and its values, update it and then insert it again.
You can also do it by implementing Comparator as anonymous inner type and override compare() to return desired comparison.
public class TreeMaps
{
public static void main(String[] args)
{
Custom c1 = new Custom(1,"A");
Custom c2 = new Custom(3,"C");
Custom c3 = new Custom(2,"B");
TreeMap<Custom , Integer > tree = new TreeMap<Custom, Integer> (new Comparator<Custom>() {
#Override
public int compare(Custom o1, Custom o2) {
return o1.rank - o2.rank;
}
});
tree.put(c1, 1);
tree.put(c2, 2);
tree.put(c3, 3);
System.out.println(tree);
}
}
class Custom
{
int rank ;
String name ;
public Custom(int rank , String name) {
this.rank = rank ;
this.name = name ;
}
#Override
public String toString()
{
return "Custom[" + this.rank + "-" + this.name + "]" ;
}
}

Categories