【问题标题】:How to use Comparator with a multiple field object?如何将比较器与多字段对象一起使用?
【发布时间】:2016-11-12 11:32:14
【问题描述】:

我正在尝试使用接口比较器来排序优先队列,因此乘客的顺序首先取决于他们是否有残疾,然后取决于他们拥有的机票类型,最后取决于到达时间。

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;
    }   

}

这里我初始化了乘客数组,这里是Passenger类和比较方法:

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 
        }
    }
}

我怎样才能更好地处理这些多层次的比较?

【问题讨论】:

  • 那么具体是什么问题呢?代码会抛出错误吗?它没有正确排序吗?
  • 您作为构造函数参数传递的比较器为空

标签: java


【解决方案1】:

您的问题似乎是关于简化比较,但我认为您宁愿实现Comparable&lt;Passenger&gt; 而不是Comparator,并使用#compareTo 方法。至于清理,如果您只是抽象实际的布尔逻辑,那也有点容易:

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
}

这允许您为 Passenger 定义自然排序,并且被封装/在您的类实现内部(不需要太多的暴露)。

这也让排序等操作变得更加容易:

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

如果你想提供一个可以完成替代排序的 Comparator,你也可以在你的类中这样做:

public class Passenger {

    //...

    public static class ArrivalComparator implements Comparator<Passenger> {

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

    //...

}

使用我们之前的示例,您可以根据到达时间对所有乘客进行排序:

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

此外,这可以使用 Java 8 内联:

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

但总的来说,请记住 Comparator 主要用于定义特定排序,而 Comparable 定义一般/自然排序。

【讨论】:

  • 您的 compareTo 方法太复杂且易于维护,其他选项一次只能进行一次比较,如果您提到它们需要一起使用以达到预期的结果会很好
  • 其实不算太复杂,其实我觉得比原来OP里的#compare要清晰很多。其他选项一次不会只做一个,您可以根据自己的喜好严格比较它们。而且它们根本不需要一起使用,#compareTo 可以在大多数情况下代替Comparator 使用(包括Collections#sortPriorityQueue 的构造函数),我的观点是Comparator 应该是针对特定排序而不是 OP 发布的自然排序。
  • 嘿,非常感谢,我使用了您的代码并进行了一些更改,只需看看我自己的答案,真的,非常感谢。
【解决方案2】:

我认为您正在寻找的是重构您的代码,我建议将 compare 逻辑分离到一个单独的 PassengerComparator 类 (SRP) 以获得更好的维护可读性 如下图。

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();
     }
}

用法:

PriorityQueue<Book> queue = new PriorityQueue<Book>(10, new PassengerComparator());

【讨论】:

  • 不会是PassengerComparator implements Comparator&lt;Passenger&gt;,而你会初始化为PassengerComparator comp = new PassengerComparator();吗?
【解决方案3】:

我怎样才能更好地处理这些多层次的比较?

  1. 为每个属性创建单独的比较器。
  2. 将单个比较器组合成多级比较。查看 Group Comparator 获取此方法的示例。

所以知道你有可重用的代码,允许你按你希望的任何顺序进行排序,而无需编写复杂的多级比较器。

您可能还想查看Bean Comparator,它可以轻松地在一行代码中创建单独的比较器。

【讨论】:

    【解决方案4】:

    嘿,我刚刚得到了正确的答案,只需将其添加到“乘客”类中,它就可以按照我想要的确切方式进行,因此偏好顺序是:残疾、类和到达时间,非常感谢。 :)

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 2010-09-27
      • 1970-01-01
      • 1970-01-01
      • 2012-04-07
      • 1970-01-01
      • 2011-08-18
      • 2020-05-06
      相关资源
      最近更新 更多