With Java 8:
Comparator.comparing((Person p)->p.firstName)
.thenComparing(p->p.lastName)
.thenComparingInt(p->p.age);
If you have accessor methods:
Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge);
If a class implements Comparable then such comparator may be used in compareTo method:
@Override
public int compareTo(Person o){
return Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge)
.compare(this, o);
}
With Java 8:
Comparator.comparing((Person p)->p.firstName)
.thenComparing(p->p.lastName)
.thenComparingInt(p->p.age);
If you have accessor methods:
Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge);
If a class implements Comparable then such comparator may be used in compareTo method:
@Override
public int compareTo(Person o){
return Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge)
.compare(this, o);
}
You should implement Comparable <Person>. Assuming all fields will not be null (for simplicity sake), that age is an int, and compare ranking is first, last, age, the compareTo method is quite simple:
public int compareTo(Person other) {
int i = firstName.compareTo(other.firstName);
if (i != 0) return i;
i = lastName.compareTo(other.lastName);
if (i != 0) return i;
return Integer.compare(age, other.age);
}
Videos
The “different member variables” is irrelevant. It’s an implementation detail. What you need is a set of rules which of two people comes first.
You could for example sort by family name, then given name, then date of birth, and if these are all three equal, take the name of the school, university or company (which will be different member variables) and compare them as strings. If that is equal, you might have student and employee ids, and the student ids might be unique, and the employee ids might be unique, but student and employee ids might be the same. So you could sort then students first ordered by id, followed by employees sorted by id, if you might sort by if first if student and employee ids are comparable.
(University or school and employer might be the same, because universities are also employers).
Comparing objects with different fields sounds like bad polymorphic design, whether it's Java or any other OOP language:
- If your comparator needs to know the precise subtype of an object to do the comparison, you mess-up with the the open-closed principle, since for every new subclassing, you'd potentially need to modify the comparator to select the relevant fields.
- If your comparator needs uses reflexion to find on its own the relevant fields to compare, you indirectly mess up with the principle of encapsulation, since you create a hidden requirement that information to be compared must be in some predetermined field.
If you want to sort People properly in a clean polymorphic design:
- you need to rely either on a field, available for any kind of
People, includingStudent, or - you may call some function/transformation that provides a unique value (e.g. a string) that allows to sort any
People.PeopleandStudentmay then just use a different transformation that will be passed to the comparator; Or - you only sort among homogeneous subtypes.
There's a very hacky way of doing it that allows you to use Object and instanceof but if you can implement a proxy class that exposes a specific interface you would be better off doing that.
class A {
public String getSomething() {
return "A";
}
}
class B {
public String getSomethingElse() {
return "B";
}
}
class C implements Comparator<Object> {
@Override
public int compare(Object o1, Object o2) {
// Which is of what type?
A a1 = o1 instanceof A ? (A) o1: null;
A a2 = o2 instanceof A ? (A) o2: null;
B b1 = o1 instanceof B ? (B) o1: null;
B b2 = o2 instanceof B ? (B) o2: null;
// Pull out their values.
String s1 = a1 != null ? a1.getSomething(): b1 != null ? b1.getSomethingElse(): null;
String s2 = a2 != null ? a2.getSomething(): b2 != null ? b2.getSomethingElse(): null;
// Compare them.
return s1 != null ? s1.compareTo(s2): 0;
}
}
The more acceptable mechanism would be to implement a proxy class for each that implements a common interface tyhat can then be compared using a proper type-safe comparator.
interface P {
public String getValue();
}
class PA implements P {
private final A a;
PA(A a) {
this.a = a;
}
@Override
public String getValue() {
return a.getSomething();
}
}
class PB implements P {
private final B b;
PB(B b) {
this.b = b;
}
@Override
public String getValue() {
return b.getSomethingElse();
}
}
class PC implements Comparator<P> {
@Override
public int compare(P o1, P o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
If you store objects of the two unrelated types in a Set (which makes me wonder, since Sets are usually unordered), this means you are using a raw Set, which is not very type safe.
You can define an interface CommonAB that contains getter methods for all the common properties of A and B. Both A and B will implement that interface. You'll be able to put instances of A and B in a Set<CommonAB>.
Finally, a Comparator<CommonAB> will be able to compare objects of both types. The Comparator will access only the methods of the interface. It won't care if it's comparing two As, two Bs or an A and a B.