【问题标题】:When to use Comparable and Comparator何时使用 Comparable 和 Comparator
【发布时间】:2011-01-17 00:47:06
【问题描述】:

我有一个需要在字段上排序的对象列表,比如分数。没有多想,我写了一个实现 Comparator 的新类,它完成了任务并且它工作。

现在回头看,我想知道是否应该让我的类实现 Comparable,而不是创建一个实现 Comparator 的新类。分数是对象将被排序的唯一字段。

  1. 我做了什么可以接受的做法?

  2. 正确的方法是“首先让类实现 Comparable(用于自然排序),如果需要替代字段比较,则创建一个实现 Comparator 的新类”?

  3. 如果上面的(2)为真,那么是否意味着只有在类实现 Comparable 之后才应该实现 Comparator? (假设我拥有原始课程)。

【问题讨论】:

    标签: java comparator comparable


    【解决方案1】:

    比较同一类的实例时应使用 Comparable。

    Comparator 可用于比较不同类的实例。

    Comparable 由需要为其对象定义自然顺序的类实现。 例如,String 实现 Comparable。

    如果需要不同的排序顺序,则实现比较器并定义自己的方式来比较两个实例。

    【讨论】:

      【解决方案2】:

      如果对象的排序需要基于自然顺序,则使用 Comparable,而如果您需要对不同对象的属性进行排序,则使用 Java 中的 Comparator。

      Comparable 和 Comparator 的主要区别:

      +------------------------------------------------------------------------------------+
      ¦               Comparable                ¦                Comparator                ¦
      ¦-----------------------------------------+------------------------------------------¦
      ¦ java.lang.Comparable                    ¦ java.util.Comparator                     ¦
      ¦-----------------------------------------+------------------------------------------¦
      ¦ int objOne.compareTo(objTwo)            ¦ int compare(objOne, objTwo)              ¦
      ¦-----------------------------------------+------------------------------------------¦
      ¦ Negative, if objOne < objTwo            ¦ Same as Comparable                       ¦
      ¦ Zero,  if objOne == objTwo              ¦                                          ¦
      ¦ Positive,  if objOne > objTwo           ¦                                          ¦
      ¦-----------------------------------------+------------------------------------------¦
      ¦ You must modify the class whose         ¦ You build a class separate from to sort. ¦
      ¦ instances you want to sort.             ¦ the class whose instances you want       ¦
      ¦-----------------------------------------+------------------------------------------¦
      ¦ Only one sort sequence can be created   ¦ Many sort sequences can be created       ¦
      ¦-----------------------------------------+------------------------------------------¦
      ¦ Implemented frequently in the API by:   ¦ Meant to be implemented to sort          ¦
      ¦ String, Wrapper classes, Date, Calendar ¦ instances of third-party classes.        ¦
      +------------------------------------------------------------------------------------+
      

      【讨论】:

        【解决方案3】:

        Comparable 是为数值提供的默认自然排序顺序为升序,而字符串为字母顺序。 例如:

        Treeset t=new Treeset();
        t.add(2);
        t.add(1);
        System.out.println(t);//[1,2]
        

        Comparator 是自定义 myComparator 类中通过重写 compare 方法实现的自定义排序顺序 例如:

        Treeset t=new Treeset(new myComparator());
        t.add(55);
        t.add(56);
        class myComparator implements Comparator{
        public int compare(Object o1,Object o2){
        //Descending Logic
        }
        }
        System.out.println(t);//[56,55]
        

        【讨论】:

          【解决方案4】:

          Comparable - java.lang.Comparable: int compareTo(Object o1)

          一个可比较的对象能够将自己与另一个对象进行比较。类本身必须实现 java.lang.Comparable 接口才能比较其实例。

          • 能够将当前对象与提供的对象进行比较。
          • 通过使用这个我们可以实现基于实例属性的only one sort sequence。 例如:Person.id
          • 一些预定义类,如 String、Wrapper 类、Date、Calendar 已经实现了 Comparable 接口。

          Comparator - java.util.Comparator: int compare(Object o1, Object o2)

          比较器对象能够比较两个不同的对象。该类不比较它的实例,而是比较其他类的实例。这个比较器类必须实现 java.util.Comparator 接口。

          • 能够比较任何两个相同类型的对象。
          • 通过使用它,我们可以实现 many sort sequence 并根据实例属性命名每个。 例如:Person.id, Person.name, Person.age
          • 我们可以为我们的预定义类实现 Comparator 接口以进行自定义排序。

          例子:

          public class Employee implements Comparable<Employee> {
          
              private int id;
              private String name;
              private int age;
              private long salary;
          
              // Many sort sequences can be created with different names.
              public static Comparator<Employee> NameComparator = new Comparator<Employee>() {         
                  @Override
                  public int compare(Employee e1, Employee e2) {
                      return e1.getName().compareTo(e2.getName());
                  }
              };
              public static Comparator<Employee> idComparator = new Comparator<Employee>() {       
                  @Override
                  public int compare(Employee e1, Employee e2) {
                      return Integer.valueOf(e1.getId()).compareTo(Integer.valueOf(e2.getId()));
                  }
              };
          
              public Employee() { }
              public Employee(int id, String name, int age, long salary){
                  this.id = id;
                  this.name = name;
                  this.age = age;
                  this.salary = salary;
              }
              // setters and getters.
          
              // Only one sort sequence can be created with in the class.
              @Override
              public int compareTo(Employee e) {
              //return Integer.valueOf(this.id).compareTo(Integer.valueOf(e.id));
              //return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
                  if (this.id > e.id) {
                      return 1;
                  }else if(this.id < e.id){
                      return -1;
                  }else {
                      return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
                  }
          
              }   
          
              public static void main(String[] args) {
          
                  Employee e1 = new Employee(5, "Yash", 22, 1000);
                  Employee e2 = new Employee(8, "Tharun", 24, 25000);
          
                  List<Employee> list = new ArrayList<Employee>();
                  list.add(e1);
                  list.add(e2);
                  Collections.sort(list); // call @compareTo(o1)
                  Collections.sort(list, Employee.nameComparator); // call @compare (o1,o2)
                  Collections.sort(list, Employee.idComparator); // call @compare (o1,o2)
              }
          }
          
          • 对于自定义排序,我们使用比较器@compare(o1, o2),对于其他场景,我们使用可比较的@compareTo(o1),如果我们想要对多个字段进行排序,则无需更改代码,然后使用比较器。

          Java 8 Lambda : Comparator请参考我的帖子。

          【讨论】:

            【解决方案5】:

            以下几点可以帮助您决定在哪些情况下应该使用 Comparable 以及在哪些情况下使用 Comparator:

            1) 代码可用性

            2) 单对多排序标准

            3) Arays.sort() 和 Collection.sort()

            4) 作为 SortedMap 和 SortedSet 中的键

            5) 更多类与灵活性

            6) 类间比较

            7) 自然秩序

            更详细的文章可以参考When to use comparable and when to use comparator

            【讨论】:

            • 我想知道为什么没有人支持这个答案。这是一个非常好的。 +1
            【解决方案6】:

            在一次采访中,我被要求在比 nlogn 时间更好的时间内对一定范围的数字进行排序。 (不使用计数排序)

            在对象上实现 Comparable 接口允许隐式排序算法使用重写的 compareTo 方法对排序元素进行排序,这将是线性时间。

            【讨论】:

              【解决方案7】:

              使用Comparable:

              • 如果对象在您的控制之下。
              • 如果比较行为是主要比较行为。

              使用Comparator

              • 如果对象不在您的控制范围内并且您无法让它们实现Comparable
              • 当您想要比较与默认行为(由Comparable 指定)不同的行为时。

              【讨论】:

                【解决方案8】:

                Comparator 做了所有可比较的事情,而且更多。

                | | Comparable | Comparator ._______________________________________________________________________________ Is used to allow Collections.sort to work | yes | yes Can compare multiple fields | yes | yes Lives inside the class you’re comparing and serves | | as a “default” way to compare | yes | yes Can live outside the class you’re comparing | no | yes Can have multiple instances with different method names | no | yes Input arguments can be a list of | just Object| Any type Can use enums | no | yes

                我发现将比较器用作匿名类的最佳方法如下:

                private static void sortAccountsByPriority(List<AccountRecord> accounts) {
                    Collections.sort(accounts, new Comparator<AccountRecord>() {
                
                        @Override
                        public int compare(AccountRecord a1, AccountRecord a2) {
                            return a1.getRank().compareTo(a2.getRank());
                        }
                    });
                }
                

                您可以在计划排序的类中创建此类方法的多个版本。所以你可以:

                • sortAccountsByPriority
                • 按类型排序帐户
                • sortAccountsByPriorityAndType

                  等等……

                现在,您可以在任何地方使用这些排序方法并获得代码重用。 这给了我所有可比较的东西,加上更多......所以我看不出有任何理由使用可比较的。

                【讨论】:

                  【解决方案9】:

                  如果您拥有该课程,最好选择 Comparable。通常,如果您不拥有该类,则使用 Comparator 但您必须将其用作 TreeSetTreeMap 因为 Comparator 可以作为参数传递TreeSet 或 TreeMap 的构造函数。您可以在http://preciselyconcise.com/java/collections/g_comparator.php中看到如何使用 Comparator 和 Comparable

                  【讨论】:

                    【解决方案10】:

                    可比:
                    每当我们只想存储同质元素和需要默认的自然排序顺序时,我们可以去实现comparable接口的类。

                    比较器:
                    每当我们想要存储同质和异质元素并且想要按照默认的自定义排序顺序进行排序时,我们可以使用comparator接口。

                    【讨论】:

                      【解决方案11】:

                      我的需求是按日期排序。

                      所以,我使用了 Comparable,它对我来说很容易。

                      public int compareTo(GoogleCalendarBean o) {
                          // TODO Auto-generated method stub
                          return eventdate.compareTo(o.getEventdate());
                      }
                      

                      Comparable 的一个限制是它们不能用于列表以外的集合。

                      【讨论】:

                        【解决方案12】:

                        用于实现ComparableComparator 的注释库:

                        public class Person implements Comparable<Person> {         
                            private String firstName;  
                            private String lastName;         
                            private int age;         
                            private char gentle;         
                        
                            @Override         
                            @CompaProperties({ @CompaProperty(property = "lastName"),              
                                @CompaProperty(property = "age",  order = Order.DSC) })           
                            public int compareTo(Person person) {                 
                                return Compamatic.doComparasion(this, person);         
                            }  
                        }
                        

                        点击链接查看更多示例。 http://code.google.com/p/compamatic/wiki/CompamaticByExamples

                        【讨论】:

                          【解决方案13】:

                          如果您需要自然顺序排序 -- 用户可比 如果您需要自定义订单排序 - 使用比较器

                          例子:

                          Class Employee{
                          private int id;
                          private String name;
                          private String department;
                          }
                          

                          自然顺序排序将基于 id,因为它是唯一的,并且自定义顺序排序将是名称和部门。

                          参考:
                          When should a class be Comparable and/or Comparator? http://javarevisited.blogspot.com/2011/06/comparator-and-comparable-in-java.html

                          【讨论】:

                            【解决方案14】:

                            非常简单的方法是假设有问题的实体类在数据库中表示,然后在数据库表中您是否需要由实体类的字段组成的索引?如果答案是肯定的,那么实现可比较并使用索引字段进行自然排序。在所有其他情况下使用比较器。

                            【讨论】:

                              【解决方案15】:
                              • 如果在编写课程时 你只有一个排序用例 使用可比。
                              • 仅当您拥有多个 排序策略实现一个 比较器。

                              【讨论】:

                                【解决方案16】:

                                我会说:

                                • 如果比较是直观的,那么一定要实现 Comparable
                                • 如果不清楚您的比较是否直观,请使用比较器,因为它更 对于必须维护代码的可怜人来说,更明确,因此更清楚
                                • 如果可能有多个直观的比较,我更喜欢比较器, 可能由要比较的类中的工厂方法构建。
                                • 如果比较是特殊目的,请使用 Comparator

                                【讨论】:

                                  【解决方案17】:

                                  我会说一个对象应该实现 Comparable 如果这是对类进行排序的清晰自然方式,并且任何需要对类进行排序的人通常都希望这样做。

                                  但是,如果排序是对类的不寻常使用,或者排序仅对特定用例有意义,那么比较器是更好的选择。

                                  换一种方式,给定类名,是否清楚可比较的排序方式,还是您必须求助于阅读 javadoc?如果是后者,很可能未来的每个排序用例都需要一个比较器,此时比较器的实现可能会减慢类用户的速度,而不是加快他们的速度。

                                  【讨论】:

                                  • 你能举个简单的例子吗?
                                  • 这可能是一个很好的例子:gist.github.com/yclian/2627608 有使用 ComparableVersion 的版本类。版本 - 提供工厂方法 ComparableVersion 应该是对象(没有静态方法) - 提供能够与另一个版本进行比较的版本。职责分开。
                                  • 面试官问,既然 Comparable 也能做到,为什么还要使用 Comparator,我傻了 :(
                                  • @aLearner 链接已失效
                                  【解决方案18】:

                                  如果要定义相关对象的默认(自然)排序行为,请使用Comparable,通常的做法是使用对象的技术或自然(数据库?)标识符为此。

                                  如果要定义外部可控排序行为,请使用Comparator,这可以覆盖默认排序行为。

                                  【讨论】:

                                  • 这是一个技术性的解释,尽管它们是正确的,但它并没有真正说明最佳实践。
                                  • 它告诉 何时 使用每个 - 如果这不是最佳实践,那是什么?
                                  • "实现Comparable 是否意味着我正在定义自然顺序?" ,这给了我正在寻找的答案。谢谢:)
                                  【解决方案19】:

                                  这里也有类似的问题:When should a class be Comparable and/or Comparator?

                                  我想说的是: 为自然排序之类的东西实现 Comparable,例如基于内部 ID

                                  如果您有更复杂的比较算法,请实施 Comparator,例如多个字段等等。

                                  【讨论】:

                                  猜你喜欢
                                  • 2014-04-04
                                  • 2012-11-12
                                  • 2015-09-06
                                  • 2013-01-28
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2020-11-22
                                  • 1970-01-01
                                  相关资源
                                  最近更新 更多