I am having issues finding an efficient way to sort classes by order. My following code completes the order in which i need to sort, but I believe there is another way (one I dont know).
What is an efficient way to sort classes?
public int compare(Object one, Object two)
{
//S = Salaried, W = Weekly, D = Daily
//SS == 0 -> SW == -1 -> SD == -1
//WS == 1 -> WW == 0 -> WD == -1
//DS == 1 -> DW == 1 -> DD == 0
Employee a = (Employee)one;
Employee b = (Employee)two;
SalariedEmployee s = new SalariedEmployee(0.0);
WeeklyEmployee w = new WeeklyEmployee (0.0);
DailyEmployee d = new DailyEmployee();
if(one.getClass() == s.getClass() && two.getClass() == s.getClass())
return Double.compare(b.grossPay(), a.grossPay());
if(one.getClass() == s.getClass() && two.getClass() == w.getClass())
return -1;
if(one.getClass() == s.getClass() && two.getClass() == d.getClass())
return -1;
if(one.getClass() == w.getClass() && two.getClass() == s.getClass())
return 1;
if(one.getClass() == w.getClass() && two.getClass() == w.getClass())
return Double.compare(b.grossPay(), a.grossPay());
if(one.getClass() == w.getClass() && two.getClass() == d.getClass())
return -1;
if(one.getClass() == d.getClass() && two.getClass() == s.getClass())
return 1;
if(one.getClass() == d.getClass() && two.getClass() == w.getClass())
return 1;
if(one.getClass() == d.getClass() && two.getClass() == d.getClass())
return Double.compare(b.grossPay(), a.grossPay());
return 0;
}
implement Comparable<> interface in your class and override compareTo() method in Employee class. the method takes Object class as a passed value. For example,
public class Employee implements Comparable<Employee> {
//omitted
public int compareTo(Employee other) {
return grossPay.compareTo(other.grossPay);
}
}
check out the following link to learn more
http://download.oracle.com/javase/tutorial/collections/interfaces/order.html
I am having issues finding an efficient way to sort classes by order
Depends what you mean by "efficient". Putting all the code in a single method will be the most efficient (if done properly) from a CPU perspective, but it is not very efficient from a flexibility point of view.
For an approach that will not be a fast but will be far more flexible check out the Group Comparator and Bean Comparator.
The GroupComparator allows you to combine multiple Comparators into one sort. The BeanComparator is a generic comparator that allows you to sort on any field within a given class. So to use the GroupComparator the basic code would be:
EmployeeComparator employee = new EmployeeComparator();
BeanComparator grossPay = new BeanComparator(Employee.class, "grossPay");
GroupComparator gc = new GroupComparator(employee, grossPay);
Collections.sort(list, gc);
So you would need to write a Comparator that sorts Employees by salaried, weekly and daily. The basic code for the EmployeeComparator might be something like:
if (one.getClass()equals(two.getClass())
return 0;
if (one instanceOf SalariedEmployee)
return 1;
if (two instanceOf SalariedEmployee)
return -1;
if (one instanceOf WeeklyEmployee)
return 1;
else
return -1;
A little more work to set up, but once you have an EmployeeComparator you can then sort of multiple different properties using the Bean and Group Comparators.
Here is my solution.
public int compare(Employee left, Employee right) {
int typeOrderLeft = getTypeOrder(left);
int typeOrderRight = getTypeOrder(right);
if (typeOrderLeft == typeOrderRight) {
return Double.compare(left.grossPay(), right.grossPay());
} else {
return typeOrderLeft - typeOrderRight;
}
}
private int getTypeOrder(Employee employee) {
if (employee instanceof DailyEmployee) {
return 1;
} else if (employee instanceof WeeklyEmployee) {
return 2;
} else if (employee instanceof SalaryEmployee) {
return 3;
}
return 0;
}
You will need to first implement the comparable interface. This lets you define a compareTo method which can be used to sort classes based on a specificed value that you think is viable for comparing classes.
Defining the compareTo method is useful for objects that don't have a predefined way of comparing themselves.
Related
What is the best way to deal with null values, when doing Collections.sort() on nested objects?
I'd like to sort a couple of objects, basically applying this rule:
#Override
public int compare(final InvoicePos invoicePosOne, final InvoicePos invoicePosTwo) {
return invoicePosOne.getInvoice().getInvoiceNo().compareTo(invoicePosTwo.getInvoice().getInvoiceNo());
}
However, any of these objects can be null (i.e. invoice position, invoice and invoice number).
public class InvoicePos {
private Invoice invoice = null;
// ...
}
public class Invoice {
private String invoiceNo = "";
// ...
}
Do I have do do explicit null-checks on all my objects or is there an approach with less writing?
For clarification: I'm aware that my above example may raise NullPointerExceptions. Currently I'm doing the following and basically, I questioned myself, if there is any smarter approach.
Collections.sort(allInvoicePositions, new Comparator<InvoicePos>() {
#Override
public int compare(final InvoicePos invoicePosOne, final InvoicePos invoicePosTwo) {
if (null == invoicePosOne && null == invoicePosTwo) {
return 0;
}
if (null == invoicePosOne) {
return -1;
}
if (null == invoicePosTwo) {
return 1;
}
if (null == invoicePosOne.getInvoice() && null == invoicePosTwo.getInvoice()) {
return 0;
}
if (null == invoicePosOne.getInvoice()) {
return -1;
}
if (null == invoicePosTwo.getInvoice()) {
return 1;
}
if (null == invoicePosOne.getInvoice().getInvoiceNo() && null == invoicePosTwo.getInvoice().getInvoiceNo()) {
return 0;
}
if (null == invoicePosOne.getInvoice().getInvoiceNo()) {
return -1;
}
if (null == invoicePosTwo.getInvoice().getInvoiceNo()) {
return 1;
}
return invoicePosOne.getInvoice().getInvoiceNo().compareTo(invoicePosTwo.getInvoice().getInvoiceNo());
}
});
There is something called as NullComparator in org.apache.commons.collections.jar.
This might help you https://commons.apache.org/proper/commons-collections/javadocs/api-2.1.1/org/apache/commons/collections/comparators/NullComparator.html.
Do I have do do explicit null-checks on all my objects or is there an approach with less writing?
If these values don't represent anything in your collection, then the best thing you can do is avoid them; don't allow inserting them, so you won't have to handle them when comparing items.
If you insist to have them, then you must check if they're null to avoid NullPointerException.
If you have null values then you need to handle them explicitly and in a consistent way so to have a valid ordering relation. That is, something like:
compare (a, b) {
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return +1;
return comp(a,b);
}
Don't be tempted to do something like:
compare (a, b) {
if (a == null || b == null) return -1;
return comp(a,b);
}
which would break the ordering relation.
What I need is: Verify if an object exist in a List comparing some attributes.
I'm in a trouble here with Collections and Comparator. I'm trying to do the verify with this Binary Search:
Collections.binarySearch(listFuncionarioObs2, formFuncionarioObsIns, formFuncionarioObsIns.objectComparator);//Binary search of an object in a List of this Object.
With this comparator:
public int compare(FuncionarioObs func, FuncionarioObs funcToCompare) {
int testCodigo = -1;
if(null != func2.getCodigo()){
testCodigo = func.getCodigo().compareTo(funcToCompare.getCodigo());
}
int testData = func.getData().compareTo(funcToCompare.getData());
int testEvento = func.getEvento().compareTo(funcToCompare.getEvento());
int testAndamento = func.getAndamento().compareTo(funcToCompare.getAndamento());
if(testCodigo == 0 && testData == 0 && testEvento == 0 && testAndamento == 0){
return 0;
}else if(testData == 0 && testEvento == 0 && testAndamento == 0) {
return 0;
}
return -1;
}
But I'm a little bit lost, this is not working and I don't know the best way to do this. Someone can turn on a light for me?
Best regards,
Edited.
I'm sorting the List before the Binary Search with this code:
List<FuncionarioObs> listFuncionarioObsBD = funcionarioObsDAO.getFuncionarioObsById(sigla);
Collections.sort(listFuncionarioObsBD);
The comparator to the sort is:
#Override
public int compareTo(FuncionarioObs func) {
if(this.getCodigo() > func.getCodigo()){
return 1;
}else if(this.getCodigo() == func.getCodigo() ) {
return 0;
}else{
return -1;
}
}
CompareTo
Your compare wont work correctly. Right now it is only comparing the references of the objects. You will have to change this to compare the objects values:
#Override public int compareTo(Account aThat) {
final int BEFORE = -1;
final int EQUAL = 0;
final int AFTER = 1;
//this optimization is usually worthwhile, and can
//always be added
if (this == aThat) return EQUAL;
//primitive numbers follow this form
if (this.fAccountNumber < aThat.fAccountNumber) return BEFORE;
if (this.fAccountNumber > aThat.fAccountNumber) return AFTER;
//booleans follow this form
if (!this.fIsNewAccount && aThat.fIsNewAccount) return BEFORE;
if (this.fIsNewAccount && !aThat.fIsNewAccount) return AFTER;
.
.
.
//all comparisons have yielded equality
//verify that compareTo is consistent with equals (optional)
assert this.equals(aThat) : "compareTo inconsistent with equals.";
return EQUAL;
}
from here
Finding the object
Now comes the next part. As CrtlAltDelete has hinted it dependends of whether your list is sorted or not.
If its sorted ascending: iterate through the objects till you either find one which compareTo returns a Zero (== success) or a One ( == fail).
For an unsorted list you will have to iterate through all objects in search for one that returns a Zero.
I have got this code, and I get an error incomparable types: java.lang.String and int, for this line of code
if ((this.name.String.compareTo(obj.name == 0)) && (this.age = obj.age))
The method is this:
public int compareTo(Object o)
{
int result;
AnyClass obj = (AnyClass)o;
if ((this.name.String.compareTo(obj.name == 0)) && (this.age = obj.age))
{
result = 0;
}
else if (this.name.compareTo(obj.name) > 0)
{
result = 1;
}
else
{
result = -1;
}
return result;
}
I think that position of your bracket isn't correct,
this.name.String.compareTo(obj.name == 0))
obj.name == 0 is the place where you probably compare String (name) to int (0). I guess you wanted to use compareTo on obj.name and then check if it's equal to zero.
I also think that in the second part
(this.age = obj.age)
You wanted to use == instead of =, so I think that the code you wanted to use is:
((this.name.compareTo(obj.name)==0) && (this.age == obj.age))
You can't compare a string to an integer :)
You can convert the string "001" into the integer "1"; or the integer "1" into the string "1".
See Integer.parseInt() or Integer.toString().
compareTo takes in Objec reference(a string in your case) as argument. But your code compareTo(obj.name == 0) passes in boolean which is not appropriate.
I think the code
if ((this.name.String.compareTo(obj.name == 0)) && (this.age = obj.age))
is actually supposed to read like this
if ((this.name.compareTo(obj.name) == 0) && (this.age == obj.age))
Changing where the == 0 is (and changing the second = to an ==) makes this code make sense.
There are a lot of problems with this implementation. It looks like this is an implementation of Comparable for class AnyClass which means the signature is wrong.
AnyClass should implement Comparable<AnyClass>, and the code should look like this:
#Override
public int compareTo(AnyClass other)
{
int ret = name.compareTo(other.name);
return ret != 0 ? ret : Integer.compare(age, other.age);
}
If you use Guava:
#Override
public int compareTo(AnyClass other)
{
return ComparisonChain.start().compare(name, other.name)
.compare(age, other.age).result();
}
((this.name.String.compareTo(obj.name == 0)) && (this.age = obj.age))
obj.name is a String, and 0 is an int. That's where you're getting the error
obj.name is String and you are comparing it with 0.
It is easier if you explain your intention with the code. The first error seems to be the comparison in compareTo obj.name == 0.
Try
if ((this.name.String.compareTo(obj.name) == 0) && (this.age == obj.age))
Which is what I guess you want to achieve.
I have an array of a custom type that I want to sort by one of its String attributes. For some reason, the following code is producing wrong results. Could you point out where I might have made a mistake?
class PatientLNComparator implements Comparator<Patient>{
#Override
public int compare(Patient p1, Patient p2) {
String p1_LN = (p1 == null) ? null : p1.last_name;
String p2_LN = (p2 == null) ? null : p2.last_name;
if(p2_LN == null)
return -1;
else if(p1_LN == null)
return +1;
else if(p1_LN.equals(p2_LN))
return 0;
else if(p1_LN.compareTo(p2_LN) > 0)
return -1;
else
return +1;
}
}
One problem to start with - your comparator is inconsistent if you give it two patients with null names, or two null patient references. In particular:
Patient p1 = null;
Patient p2 = null;
int x = comparator.compare(p1, p2);
int y = comparator.compare(p2, p1);
The signs of x and y ought to be different - but they'll both be -1.
After that, it depends on how you want to compare the names. I would usually use
return p1_LN.compareTo(p2_LN);
if you want to sort in ascending order. Note that to sort in descending order you shouldn't just return -p1_LN.compareTo(p2_LN), as if the comparison returns the Integer.MIN_VALUE, the negation won't work. Instead you'd want to return p2_LN.compareTo(p1_LN);.
Note that if you're using this scheme, you don't need to call p1_LN.equals(p2_LN) either - that will be handled by the compareTo call.
You want patient to be ordered by alphabetical by last name, null patients and null last names up front?
class PatientLNComparator implements Comparator<Patient>{
#Override
public int compare(Patient p1, Patient p2) {
String p1_LN = (p1 == null) ? null : p1.last_name;
String p2_LN = (p2 == null) ? null : p2.last_name;
if (p1_LN == null && p2_LN == null)
return 0;
else if (p2_LN == null)
return -1;
else if(p1_LN == null)
return +1;
else
return p1_LN.compareTo(p2_LN);
}
}
To be stable, it really should order by some other fields, like first name, when last names are equal.
I'm assuming you want natural string ordering for this.
First of all, as it is, your compareTo branch is giving inversed results. Don't know if that's what you intended or not (as in you're saying p1 is greater than p2 when the p1's string is lower than p2's).
Furthermore, you can ditch the .equals branch of the if. The compareTo already handles this case.
Therefore a simple
if(p2_LN == null && p1_LN == null)
return 0;
else if(p1_LN == null)
return +1;
else if(p2_LN == null)
return -1;
else return p1_LN.compareTo(p2_LN)
would suffice.
I would use Guava's Ordering class for this:
class Patient {
// ...
public static final Function<Patient, String> GET_LAST_NAME =
new Function<Patient, String>() {
public String apply(Patient from) {
if (from == null) return null;
return from.last_name;
}
};
public static final Comparator<Patient> BY_LAST_NAME =
Ordering.natural()
.onResultOf(GET_LAST_NAME)
.nullsFirst();
}
This will resolve the issue with inconsistent comparison of nulls. It also makes it easy to add a secondary order (e.g. first name):
public static final Comparator<Patient> BY_LAST_NAME =
Ordering.natural()
.onResultOf(GET_LAST_NAME)
.compound(Ordering.natural().onResultOf(GET_FIRST_NAME))
.nullsFirst();
I want to compare two object based on 5-tuple which are:
srcAddr, dstAddr, srcPort, dstPort, protocol
here is what i have:
public class Flows implements Serializable, Comparable {
String srcAddr, dstAddr, srcPort, dstPort, protocol;
public int compareTo(Flows arg0) {
if(this.srcAddr == arg0.srcAddr &&
this.dstAddr == arg0.dstAddr &&
this.srcPort == arg0.srcPort &&
this.dstPort == arg0.dstPort &&
this.protocol == arg0.protocol)
return 0;
}
}
But it doesn't work. It says can not compare two strings.
Can anyone help me to know what is the problem?
Thanks.
The compiler / code checker is warning you that comparing String values with == is almost always a mistake.
But fixing that won't really help because your code does nothing like what a correctly implemented compareTo method should do.
A straight-forward implementation of compareTo for your Flows class would be:
public int compareTo(Flows other) {
int res = this.srcAddr.compareTo(other.srcAddr);
if (res != 0) {
return res;
}
res = this.dstAddr.compareTo(other.dstAddr);
if (res != 0) {
return res;
}
res = this.srcPort.compareTo(other.srcPort);
if (res != 0) {
return res;
}
res = this.dstPort.compareTo(other.dstPort);
if (res != 0) {
return res;
}
return this.protocol.compareTo(other.protocol);
}
That assumes the the fields are never null. If they are, then write a safeCompare(String, String) method that takes care with nulls and apply it to each field as above.
EDIT
Given that you are defining compareTo you also ought to declare equals and hashCode to be consistent with them. Otherwise certain collection methods are likely to behave incorrectly.
EDIT 2
The compiler error you mention in a comment on how to override compareTo method happens because the int compareTo(Flow flow) method actually implements the compareTo method of Comparable<Flow>. If you are going to declare Flow as implementing the raw interface type Comparable then the signature needs to be
public int compareTo(Object obj) {
Flow flow = (Flow) obj;
...
But a better solution would be to change the class declaration to:
public class Flows implements Serializable, Comparable<Flow> {
...
Try:
#Override
public int compareTo(final Flows that) {
return ComparisonChain.start().
compare(this.srcAddr, that.srcAddr).
compare(this.dstAddr, that.dstAddr).
compare(this.srcPort, that.srcPort).
compare(this.dstPort, that.dstPort).
compare(this.protocol, that.protocol).
result();
}
Requires Guava
Use string.equals() instead of ==.
You can write like this also, I have done like this in my project
public int compareTo(Flows arg0) {
int comp1, comp2, comp3, comp4;
comp1 = this.srcAddr.compareTo(arg0.srcAddr);
comp2 = this.dstAddr.compareTo(arg0.dstAddr);
comp3 = this.srcPort.compareTo(arg0.srcPort);
comp4 = this.protocol.compareTo(arg0.protocol);
if (comp1 == 0 && comp2 == 0 && comp3 == 0 && comp4 == 0) {
return 0;
} else {
if (comp1 != 0)
return comp1;
else {
if (comp2 != 0)
return comp2;
else {
if (comp3 != 0)
return comp3;
else {
if (comp4 != 0)
return comp4;
else
return 0;
}
}
}
}
}