Beka Bekeri Beka Bekeri - 19 days ago 4
Java Question

How to use Comparator with a multiple field object?

Im trying to use the interface comparator to order a priority queue so the oorder of the passengers depends in first levell if they have disability, then depending of the type of ticket they have and lastly the time of arrival.

import java.util.*;

public static void main(String[] args){
Random rand = new Random(System.nanoTime());
Comparator<Passenger> comparator;
PriorityQueue<Passenger> queue = new PriorityQueue<Passenger>(10, comparator);
Passenger pass[] = new Passenger [10];


for (int i=0; i<10;i++){
int time1 = 0;
pass[i] = new Passenger(rand.nextInt(100000000), rand.nextInt(3) , rand.nextBoolean(), time1);
time1 = time1 + 15;
}

}


There i initialize the array of passengers, here is the class Passenger and the compare method:

public class Passenger implements Comparator<Passenger>{

private int ID;
private int clase;
private boolean disability;
private int arrivalTime;

public Passenger(int ID, int clase, boolean disability, int arrivalTime) {

this.ID = ID;
this.clase = clase; // 0-vip 1-economy 2-economy
this.disability = disability;
this.arrivalTime = arrivalTime;
}
public int getID() {
return ID;
}
public void setID(int iD) {
ID = iD;
}
public int getClase() {
return clase;
}
public void setClase(int clase) {
this.clase = clase;
}
public boolean isDisability() {
return disability;
}
public void setDisability(boolean disability) {
this.disability = disability;
}
public int getArrivalTime() {
return arrivalTime;
}
public void setArrivalTime(int arrivalTime) {
this.arrivalTime = arrivalTime;
}

public int compare(Passenger pas1, Passenger pas2) {
if((pas1.isDisability()) && (!pas2.isDisability())){
return 1; //passenger 1 has disability
}else if((!pas1.isDisability()) && (pas2.isDisability())){
return -1; //passenger 2 has disability
}
else{ //both have disability or no one has disability
if(pas1.getClase() < pas2.getClase()){
return 1; // passenger 1 has better class
}else if(pas1.getClase() > pas2.getClase()){
return -1; // passenger 2 has better class
}
else{ //both have disability and same class
if(pas1.getArrivalTime() < pas2.getArrivalTime()){
return 1; //passenger 1 arrived before passenger 2
}
else return -1; //passenger 2 arrived before passenger 1
}
}
}


How can I handle these multi level comparisons in a better way ?

Answer

It seems your question is in regards to simplifying your comparison, but I think you'd rather implement Comparable<Passenger> rather than Comparator, and use the #compareTo method. As for cleanup, that's a bit easy too if you simply abstract the actual boolean logic:

public int compareTo(Passenger other) {
    if (this.isDisability() ^ other.isDisability()) { //use an XOR
        return this.isDisability() ? 1 : -1; //1 for us, -1 for other
    }
    //compare #getClase
    int clase = -Integer.compare(this.getClase(), other.getClase()); //invert
    if (clase == 0) {
        //compare arrival times if clase is equal
        //normalize to -1, 1 (0 excluded in OP)
        return this.getArrivalTime() < other.getArrivalTime() ? 1 : -1;
    }
    return clase > 0 ? 1 : -1; //normalize to -1, 0, 1
}

This allows you to define a natural ordering to a Passenger, and is encapsulated/internal to your class implementation (doesn't require as much exposure).

This also makes operations like sorting much easier:

List<Passenger> passengers = /* some list */;
Collections.sort(passengers);

If you want to provide a Comparator which can accomplish alternative sorts, you can also do that inside your class:

public class Passenger {

    //...

    public static class ArrivalComparator implements Comparator<Passenger> {

        public int compare(Passenger one, Passenger two) {
            return Integer.compare(one.getArrivalTime(), two.getArrivalTime());
        }
    }

    //...

}

Using our previous example, this would let you sort all the passengers based on arrival time:

Collections.sort(passengers, new Passenger.ArrivalComparator());

Additionally, this can just be inlined using Java 8:

//Sort by arrival time
Collections.sort(passengers, (one, two) -> Integer.compare(one.getArrivalTime(), two.getArrivalTime());

But overall, keep in mind a Comparator is mostly for defining a specific sort, while Comparable defines a general/natural ordering.

Comments