Reputation: 444
I can use all the simple comparators in this code for sorting just fine but not the ComplexComparator
. I couldn't figure it out how to code to get it to work properly. Any suggestion / explanation would be appreciated.
This is my main program:
package pkgTest;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Student[] students = new Student[6];
students[0] = new Student("Pete", 1989, 3.6);
students[1] = new Student("Tomas", 1989, 3.9);
students[2] = new Student("Helen", 1990, 3.6);
students[3] = new Student("Steve", 1991, 3.7);
students[4] = new Student("Natalie", 1993, 3.7);
students[5] = new Student("John", 1992, 4.0);
NameComparator byName
= new NameComparator();
BirthDateComparator byBirthDate
= new BirthDateComparator();
AverageComparator byAverage
= new AverageComparator();
ComplexComparator complexSorting
= new ComplexComparator(byName,
byAverage);
System.out.println("===============");
System.out.println("Before sorting:");
System.out.println("===============");
for (Student student : students) {
System.out.println(student.getName()
+ " // " + student.getBirthDate()
+ " // " + student.getAverage());
}
Arrays.sort(students, complexSorting);
System.out.println("==============");
System.out.println("After sorting:");
System.out.println("==============");
for (Student student : students) {
System.out.println(student.getName()
+ " // " + student.getBirthDate()
+ " // " + student.getAverage());
}
}
}
Here are the rest of the classes:
package pkgTest;
public class Student {
private String name;
private int birthDate;
private double average;
public Student(String name, int birthDate,
double average) {
this.name = name;
this.birthDate = birthDate;
this.average = average;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getBirthDate() {
return this.birthDate;
}
public void setBirthDate(int birthDate) {
this.birthDate = birthDate;
}
public double getAverage() {
return this.average;
}
public void setAverage(double average) {
this.average = average;
}
}
package pkgTest;
import java.util.Comparator;
public class ComplexComparator implements Comparator<Student> {
public ComplexComparator(Comparator<Student> one,
Comparator<Student> another) {
}
@Override
public int compare(Student one, Student another) {
/*This is the part that
I just couldn't figure
it out to get it work.
It has to work no matter
which 2 of the 3 comparators
I use to set the input
parameters of ComplexComparator.
I have to make it work by
modifying only this part of
the code.*/
}
}
package pkgTest;
import java.util.Comparator;
public class AverageComparator implements Comparator<Student> {
@Override
public int compare(Student one, Student another) {
if (one.getAverage()
< another.getAverage()) {
return -1;
} else if (one.getAverage()
== another.getAverage()) {
return 0;
} else {
return +1;
}
}
}
package pkgTest;
import java.util.Comparator;
public class BirthDateComparator implements Comparator<Student> {
@Override
public int compare(Student one, Student another) {
if (one.getBirthDate()
< another.getBirthDate()) {
return -1;
} else if (one.getBirthDate()
== another.getBirthDate()) {
return 0;
} else {
return +1;
}
}
}
package pkgTest;
import java.util.Comparator;
public class NameComparator implements Comparator<Student> {
@Override
public int compare(Student one, Student another) {
return one.getName().
compareToIgnoreCase(another.getName());
}
}
Upvotes: 2
Views: 12874
Reputation: 15042
Here is a generic complex comparator you can use for any type of of objects (based on this answer):
public class ComplexComparator<T> implements Comparator<T> {
private List<Comparator<T>> listComparators;
@SafeVarargs
public ComplexComparator(Comparator<T>... comparators) {
listComparators = Arrays.asList(comparators);
}
@Override
public int compare(T o1, T o2) {
for (Comparator<T> comparator : listComparators) {
int result = comparator.compare(o1, o2);
if (result != 0) {
return result;
}
}
return 0;
}
}
There will be an unchecked cast warning when you use it, but you can suppress that, given that it will cast successfully as long as your class is comparable.
@SuppressWarnings("unchecked")
Comparator<MyClass> comparator = new ComplexComparator(
MyClass.ComparatorA,
MyClass.ComparatorB);
Collections.sort(mySet, comparator);
If anyone knows a way how to not get that warning, please comment and I update the answer.
Upvotes: 1
Reputation: 18558
You will have to modify the class ComplexComparator
like the following, at least...
import java.util.Comparator;
public class ComplexComparator implements Comparator<Student> {
private Comparator<Student> comparatorOne;
private Comparator<Student> comparatorTwo;
public ComplexComparator(Comparator<Student> one,
Comparator<Student> another) {
this.comparatorOne = one;
this.comparatorTwo = another;
}
@Override
public int compare(Student one, Student another) {
// make a first comparison using comparator one
int comparisonByOne = comparatorOne.compare(one, another);
// check if it was 0 (items equal in that attribute)
if (comparisonByOne == 0) {
// if yes, return the result of the next comparison
return comparatorTwo.compare(one, another);
} else {
// otherwise return the result of the first comparison
return comparisonByOne;
}
}
}
For more than two Comparator
s you will need a List
of them (or another overloaded constructor) and a loop that keeps a certain order of comparisons.
EDIT
For your additional requirement regarding sorting orders, this may be helpful:
public class ComplexComparator implements Comparator<Student> {
private Comparator<Student> comparatorOne;
private Comparator<Student> comparatorTwo;
private boolean orderOneAscending = true;
private boolean orderTwoAscending = true;
/**
* Constructor without any sort orders
* @param one a comparator
* @param another another comparator
*/
public ComplexComparator(Comparator<Student> one, Comparator<Student> another) {
this.comparatorOne = one;
this.comparatorTwo = another;
}
/**
* Constructor that provides the possibility of setting sort orders
* @param one a comparator
* @param orderOneAscending sort order for comparator one
* (true = ascending, false = descending)
* @param another another comparator
* @param orderTwoAscending sort order for comparator two
* (true = ascending, false = descending)
*/
public ComplexComparator(Comparator<Student> one, boolean orderOneAscending,
Comparator<Student> another, boolean orderTwoAscending) {
this.comparatorOne = one;
this.comparatorTwo = another;
this.orderOneAscending = orderOneAscending;
this.orderTwoAscending = orderTwoAscending;
}
@Override
public int compare(Student one, Student another) {
int comparisonByOne;
int comparisonByAnother;
if (orderOneAscending) {
/* note that your lexicographical comparison in NameComparator
returns a negative integer if the String is greater!
If you take two numerical Comparators, the order will
turn into the opposite direction! */
comparisonByOne = comparatorOne.compare(another, one);
} else {
comparisonByOne = comparatorOne.compare(one, another);
}
if (orderTwoAscending) {
comparisonByAnother = comparatorTwo.compare(one, another);
} else {
comparisonByAnother = comparatorTwo.compare(another, one);
}
if (comparisonByOne == 0) {
return comparisonByAnother;
} else {
return comparisonByOne;
}
}
}
Just play around with the values and try some modifications to get familiar with common problems concerning comparing and sorting. I hope this will be helpful...
Upvotes: 3
Reputation: 187
I am not sure exactly how you want the solution to be presented. But from my understanding, if you want to do it by just putting code in the commented place, you can just try putting code like this.
Assuming the case when after comparing name, if same, you intend to move to average.
public int compare(Student StOne, Student StAnother) {
if(one.compare(Sone, Sanother)==0) {
return another.compare(StOne, StAnother);
}
else
return one.compare(StOne, StAnother);
}
But for this, you need to ensure that the values you take in the constructor of ComplexComparator (byName, byAverage) should be the instance variables of the class, and need to be initialized in the constructor.
public class ComplexComparator implements Comparator<Student> { private Comparator<Student> one; private Comparator<Student> another; public ComplexComparator(Comparator<Student> one, Comparator<Student> another) { this.one=one; this.another=another; } @Override public int compare(Student one, Student another) { //code given above } }
Upvotes: 0
Reputation: 1086
Modify Your ComplexComparator as below
public class ComplexComparator implements Comparator<Student> {
private List<Comparator<Student>> listComparators;
@SafeVarargs
public ComplexComparator(Comparator<Student>... comparators) {
this.listComparators = Arrays.asList(comparators);
}
@Override
public int compare(Student studen1, Student studen2) {
for (Comparator<Student> comparator : listComparators) {
int result = comparator.compare(studen1, studen2);
if (result != 0) {
return result;
}
}
return 0;
}
}
Upvotes: 3