1

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 ?

Vasu
  • 21,832
  • 11
  • 51
  • 67

4 Answers4

1

I think what you are looking for is refactoring your code, I would recommend separating compare logic into a separate PassengerComparator class (SRP) for better maintenance and readability as shown below.

public class PassengerComparator implements Comparator<Passenger> {

      public int compare(Passenger pas1, Passenger pas2) {    
        //check the comparison of all
        if(disabilityComparator(Passenger pas1, Passenger pas2) 
              && arrivalTimeComparator(Passenger pas1, Passenger pas2) 
              && claseComparator(Passenger pas1, Passenger pas2)) {
            return 1;
        } else {
            return -1;
        }
      }

     //compares only disability 
     private int disabilityComparator(Passenger pas1, Passenger pas2) {
            return pas1.isDisability() - pas2.isDisability();
     }

     //compares only arrivalTime 
     private int arrivalTimeComparator(Passenger pas1, Passenger pas2) {
            return pas1.getArrivalTime() - pas2.getArrivalTime();
     }  

     //compares only clase
     private int claseComparator(Passenger pas1, Passenger pas2) {
            return pas1.getClase() - pas2.getClase();
     }
}

Usage:

PriorityQueue<Book> queue = new PriorityQueue<Book>(10, new PassengerComparator());
Vasu
  • 21,832
  • 11
  • 51
  • 67
  • Wouldn't it be `PassengerComparator implements Comparator` and you'd init as `PassengerComparator comp = new PassengerComparator();`? – Rogue Nov 12 '16 at 12:19
1

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.

Rogue
  • 11,105
  • 5
  • 45
  • 71
  • Your compareTo method is too complicated and easy to maintain and the other options will do only one comparison at a time and it would be good if you mention that they need to be used together to achieve the desired result – Vasu Nov 12 '16 at 12:53
  • It's not really too complicated, in fact I think it's quite a bit clearer than the original `#compare` in the OP. The other options don't do only one at a time, you can compare them however as rigorously as to your liking. And they don't really need to be used together at all, `#compareTo` can be used in-place of a `Comparator` for most things (including `Collections#sort` and `PriorityQueue`'s constructor), and my point is that `Comparator` should be for a specific ordering vs the natural ordering the OP posted. – Rogue Nov 12 '16 at 12:55
  • Hey, thank you so much i used your code and made some changes, just take a look into my own answer, and really, thank you so much. – Beka Bekeri Nov 13 '16 at 13:41
0

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

  1. Create separate Comparators for each property.
  2. Combine individual Comparators into a multi level comparison. Check out Group Comparator for an example of this approach.

So know you have reusable code that allows you to do sorting in any order you wish without writing a complex multi level Comparator.

You may also want to check out the Bean Comparator which makes it easy to create individual Comparators in a single line of code.

camickr
  • 321,443
  • 19
  • 166
  • 288
0

Hey I just get the right answer, just by adding this to the class Passenger it works out the exact way i wanted so the preference order is: disability, class and arrivalTime, thank you so much. :)

public int compareTo(Passenger other) {
    if (this.isDisability() ^ other.isDisability()) { // use an XOR so it only enters if one of them is true
        return this.isDisability() ? -1 : 1; 
    }
     int clase = -Integer.compare(this.getClase(), other.getClase());
     if (clase == 0) {
        return this.getArrivalTime() < other.getArrivalTime() ? -1 : 1;
     }
     return clase > 0 ? -1 : 1;
}