比较器:等于方法功能

时间:2017-07-30 07:58:33

标签: java compare comparator

实际上我正在阅读其中提到的一个教程,当我们需要实现比较器接口时,我们可以覆盖equals方法(但是没有必要覆盖)。

所以只是为了更好地理解

我覆盖下面的方法

Test.java

 import java.util.TreeSet;

public class Test
{
    public static void main(String[] args)
    {
        TreeSet t = new TreeSet(new MyComparator());
        t.add(1);
        t.add(1);
        t.add(2);
        System.out.println(t);
    }
}

MyComparator.java

import java.util.Comparator;

public class MyComparator
    implements Comparator
{
    @Override
    public int compare(Object o1, Object o2)
    {
        Integer i1 = (Integer) o1;
        Integer i2 = (Integer) o2;
        return i1.compareTo(i2);
    }

    @Override
    public boolean equals(Object o1)
    {
        return false;
    }
}

用于其他方案

import java.util.Comparator;

public class MyComparator
    implements Comparator
{
    @Override
    public int compare(Object o1, Object o2)
    {
        Integer i1 = (Integer) o1;
        Integer i2 = (Integer) o2;
        return i1.compareTo(i2);
    }

    @Override
    public boolean equals(Object o1)
    {
        return true;
    }
}

现在无论我从equals方法返回的是true还是false ..它返回相同的treeset值。 如果有人能够清除equals方法的功能性概念,请

7 个答案:

答案 0 :(得分:7)

equals()上实现Comparator方法允许您指示一个比较器提供与另一个比较器相同的顺序。它与元素的排序方式无关。它是一种非常先进且极少需要的功能。您极不可能遇到实际调用比较器equals()方法的情况。我建议你忽略它。

答案 1 :(得分:2)

类的{p> equals()方法ComparatorComparator需要等于一致性,因为如果compareTo()equals()方法是一些Java集合类可能会出现不可预测的行为没有返回一致的结果。

Java使用==运算符来比较两个基元和/或检查两个变量是否引用同一个对象。 示例:

String apple1 = new String("apple");
String apple2 = new String("apple"); 
System.out.println(apple1.equals(apple2)); // true

StringBuilder app1 = new StringBuilder("apple"); 
StringBuilder app2 = new StringBuilder("apple"); 
System.out.println(app1.equals(app2));   // false

正如您所看到的,我们会得到不同的行为。这是为什么?这是因为String类实现了equals()方法,该方法检查值是否相同。另一方面,StringBuilder不实现equals()方法,而是使用Object类提供的equals()实现。由Object类提供(继承)的实现只是检查两个引用的对象是否相同。

因此要检查两个对象是否等效Java是否使用equals()方法,每当您引入自己的类型时,如果您不想依赖equals()类,则必须覆盖Object方法equals()方法

的实施

让我们举例说明我们自己的类型:简单类Apple

public class Apple  {

    private int weight;
    private int cost;
    private String color;

现在你怎么决定两个苹果是否相等?按颜色,重量,价格或其他?这就是你需要明确提供自己的equals方法的原因,因此可以比较你的类型的对象是否相等。

下面的示例比较了两个Apple对象的相等性,并说两个对象是相同的,如果它们属于同一个' Apple'如果他们的体重和成本是相同的。请注意,我们不按颜色进行比较,我们假设颜色与我们的情况无关,这意味着我们接受这样的事实:不同颜色但具有相同重量和相同成本的苹果被认为是等于的。

    @Override
    public boolean equals(Object obj) {
        if ( !(obj instanceof Apple)) return false;    
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Apple other = (Apple) obj;
        if (cost != other.cost and weight != other.weight )
            return false;
        return true;
    }

您可以为equals()方法实施任何您喜欢的逻辑。 Java equals()方法非常重要,它提供了开发人员应遵循的合同或重要规则。我不会列出它们,你从here得到它们,它们是逻辑性的并且相当直接。

还有equals()的另一个合同 - 无论何时覆盖equals(),您都应该覆盖hashCode()方法。这背后的原因是,当对象作为键存储在映射中时,某些Java集合在内部使用hashcode。

Hashcode是一个将对象分类为类别的数字。想象一下,你被给予了各种各样的苹果(红色,绿色,黄色),并被要求在要求特殊种类时给它们。如果你对它们进行分类,将它们分别放在不同的桶中,并且每当被要求进行特殊排序时(为了简单起见,请说红苹果),并且你已经对它们进行了分类和分类,你就会非常高效和快捷。更快地检索它们。希望现在很清楚为什么你需要实现hashcode()hashcode()拥有与equals()方法相同的合同或规则。他们几乎是常识:

首先,同一程序中hashcode()的结果不得改变。这意味着在hascode()计算中,您不应该包含我在程序执行期间所做更改的变量。例如,如果Apple的成本是可变的,即它可能会改变,那么将它们包含在hashcode()计算中并不是一个好主意,否则结果会不一致。

第二条规则说,当使用两个对象调用时,如果equals()返回true,则在每个对象上调用hashCode()必须检索相同的结果。但是如果equals()在使用两个对象调用时返回false,则在每个对象上调用hashCode()不一定必须返回不同的结果。混乱?为什么?因为hashCode()结果在不等对象上调用时不需要是唯一的 - 你可以在一个桶中放置两个不相等的对象。

现在关于Comparator - 当您引入自己的类型时,更容易理解它的逻辑,而不是使用内置类型。

例如,我们假设我们的类Apple有两个属性:weightprice,并希望将此类型的对象放入已排序的集合TreeSet

public class Apple  {

    private int weight;
    private int cost;

现在,您希望如何在集合内对这些苹果进行排序 - 是否要按weightprice对其进行排序?编译器应如何决定?

提供适当的ComparatorComparable可让您传递意图。 让我们尝试将对象添加到集合中。

  public class Apple {

    private int weight;
    private int cost;

  public static void main(String[] args) {
    Apple redApple = new Apple();
    redApple.setCost(10);
    redApple.setWeight(2);

    Apple greenApple = new Apple();
    greenApple.setCost(12);
    greenApple.setWeight(3);

    Set<Apple> apples = new TreeSet<>();
    apples.add(redApple);
    apples.add(greenApple);

    System.out.println(apples);
   } 

    public int getWeight() {
       return weight;
    }

   public void setWeight(int weight) {
       this.weight = weight;
    }

   public int getCost() {
       return cost;
   }

   public void setCost(int cost) {
     this.cost = cost;
    }

   @Override
   public String toString() {
       return "Apple [weight=" + weight + ", cost=" + cost + "]";
    }
}

如果你运行上面的代码,你将得到RuntimeError:Apple cannot be cast to java.lang.Comparable,因为编译器必须弄清楚你想如何比较你的苹果。 所以,让我们解决它:   让我们实现Comparable接口

  public class Apple implements Comparable<Apple> {

并覆盖compareTo方法

 @Override
    public int compareTo(Object obj) {

        int cost = ((Apple) obj).getCost();
        return this.getCost() - cost; // sorting in ascending order.

        // change to this to sort in Descending order
        // return cost - this.getCost();
    }

现在通过这些更改,让我们运行我们的代码:

[Apple [weight=2, cost=10], Apple [weight=3, cost=12]]

我们的收藏按成本按升序排序。

现在如果您无法访问Apple课程,并且无法更改源代码以实施Comparable该怎么办。

这是Comparator帮助的地方。

删除implements Comparable,因为我们假设我们无法修改此类

public class Apple {

并删除方法

@Override
public String toString() {
    return "Apple [weight=" + weight + ", cost=" + cost + "]";
}

添加比较器实现:

  public class AppleComparator implements Comparator<Apple> {

        @Override
        public int compare(Apple app1, Apple app2) {
            return app1.getCost() - app2.getCost();
        }

}

现在我可以向集合提供Comparator以表达我的意图

Set<Apple> apples = new TreeSet<>(new AppleComparator());

再次收集将按成本进行分类,相应地提供给比较器。 因此,我们需要提供ComparatorComparable才能将它们存储在集合中,尤其是在TreeSet中。

现在关于您的问题:Comparatorequals()方法之间没有关联。 Comparable(compareTo()方法)和equals()方法之间只需要一致性。

示例 - 在上面提到的Apple类中,在实现Comparable的版本中, 我们引入了确定平等的新逻辑。如果两个对象相等,compareTo()方法返回0,而如果两个对象相等,则equals()方法返回true。

当x.compareTo(y)等于0时,使用compareTo()的自然顺序必须与等于iff x.equals(y)一致。因此,您必须进行Comparable类与equals一致,因为如果compareTo()equals()方法没有返回一致的结果,某些Java集合类可能会出现不可预测的行为。

以下示例显示了与equals:

不一致的compareTo()方法
public class Apple implements Comparable<Apple> { 

private int weight;
private int cost;
private String color;

public boolean equals(Object obj) {
   if(!(obj instanceof Apple)) { 
      return false;
  }
   Apple other = (Apple) obj; 
    return this.weight == other.weight;
  }

public int compareTo(Apple obj) {
    return this.cost.compareTo(obj.cost); }
 }

如果我们想按成本对Apple对象进行排序,但成本可能并不是唯一的。可能有两个具有相同成本的对象。因此,在比较两个相等的compareTo()个对象时,Apple的返回值可能不为0,这意味着此compareTo()方法与equals()不一致。

答案 2 :(得分:1)

您覆盖的equals()方法位于MyComparator class中,因此如果您需要比较Mycomparator的2个实例,则会使用它;)

那不是你正在做的事情,你正在比较int所以compare()方法是有用的,因为它是将用于的方法sorting Treeset但是这里的等于方法不会被使用

在99%的情况下,您不需要在override equals implements的课程中使用Comparator方法,因为它们仅适用于compare个值不是他们自己到另一个人,事实上因为大部分时间他们都有属性,你不会有comparator等于。

答案 3 :(得分:1)

我不明白为什么要关联Comparatorequals()方法。像interfacesComparable这样的Comparator用于提供比较机制,就像您将数据放入专为collection设计的sorting purpose一样,然后它应该有一个比较机制。这是一个例子

package snippet;

import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

class Person {

    private int id;
    private String name;
    private String code;
    private double salary;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    public boolean equals(Object obj) {         
        if(obj != null && (obj instanceof Person)) {
            Person other = (Person) obj;
            return this.code.equals(other.getCode());
        }
        return false;
    }
    public Person(int id, String name, String code, double salary) {
        super();
        this.id = id;
        this.name = name;
        this.code = code;
        this.salary = salary;
    }
}

public class EqualsMethodImpl
{
    public static void main(String[] args) {
        Set<Person> set = new TreeSet<Person>();
        Person p1 = new Person(1, "Sam", "M-1-SAM-50", 50000.00);
        Person p2 = new Person(2, "Diaz", "M-1-SAM-35", 35000.00);
        Person p3 = new Person(3, "Remy", "M-1-SAM-100", 100000.00);
        Person p4 = new Person(4, "Cesar", "M-1-SAM-80", 80000.00);
        Person p5 = new Person(5, "Rino", "M-1-SAM-5", 5000.00);

        set.add(p1);
        set.add(p2);
        set.add(p3);
        set.add(p4);
        set.add(p5);

        printPersons(set);
    }

    private static void printPersons(Set<Person> set) {
        System.out.println("Id\tName\tCode\t\tSalary");
        Iterator<Person> perItr = set.iterator();       
        while(perItr.hasNext()) {
            Person p = perItr.next();
            System.out.println(p.getId()+"\t"+p.getName()+"\t"+p.getCode()+"\t"+p.getSalary());
        }
    }
}

在您运行此代码时,您会在第ClasCastException行获得set.add(p1);,原因是因为集合TreeSetPeron类的哪个属性应该这样做感到困惑排序。这就是interfaces ComparableComparator具有重要意义的地方。

将此代码添加到Person

public int compareTo(Person other) {
    Double person1Salary = this.getSalary();
    Double person2Salary = other.getSalary();
    return person1Salary.compareTo(person2Salary);
}

使您的类可比类型如Person implements Comparable<Person>

因此,通过此代码,您将解释我们需要对哪个属性进行排序的集合。因此,当您打印Set的内容时,它将按基本工资订购(默认为升序)。

Byte, Short, Integer, Long, Float, Double这样的Wrapper类和其他类似String的预定义类都在实现Comaparable接口,这就是为什么你可以简单地将它们传递给Sortable集合。

现在来到equals方法,它用于检查两个对象的相等性,从Object类继承的传统equals方法将根据地址位置检查相等性。因此,在Person类中,我没有覆盖equals方法,我正在编写这样的代码

Person p1 = new Person(1, "Sam", "M-1-SAM-50", 50000.00);
Person p2 = new Person(1, "Sam", "M-1-SAM-50", 50000.00);
System.out.println(p1.equals(p2));

它将返回false,因为它们是放置在两个不同地址位置的两个不同对象,但我们知道对象具有同一个人的数据。这就是我们覆盖equals()方法的地方,就像我在基于Person的{​​{1}}课程中所做的那样。在这种情况下,Person code将打印为true,因为代码相同。 System.out.println(p1.equals(p2));方法可帮助我们找到重复项。我希望这会有所帮助。

答案 4 :(得分:0)

equals方法用于检查两个比较器的相等性,即在equals方法中,您可以指定使ComparatorA和ComparatorB相同的原因,它与使用这些比较器排序的元素无关。

答案 5 :(得分:0)

您不需要对 Treeset 的值进行排序。 TreesSet 元素自动按升序排序。

class TreeSet1{  
   public static void main(String args[]){  
       //Creating and adding elements  
        TreeSet<String> al=new TreeSet<String>();  
        al.add("Ravi");  
        al.add("Vijay");  
        al.add("Ravi");  
        al.add("Ajay");  
        //Traversing elements  
       Iterator<String> itr=al.iterator();  
          while(itr.hasNext()){  
             System.out.println(itr.next());  
          }  
    }  

}

输出= 阿杰 拉维 维杰

答案 6 :(得分:-1)

首先,使用泛型。通过增加编译时类型安全性,您的代码将更加强大,这反过来将使您的生活(以及那些阅读您的代码的人)变得更加容易(当然,如果您知道自己在做什么)。您的类MyComparator实现了原始类型Comparator,这意味着通过查看其类型声明,您无法知道它比较的对象类型。您必须查看源代码(或者,在文档中),以便发现,如果传递了不属于Integer子类型的任何对象,则会抛出ClassCastException < em>在运行时。另一方面,如果要实现Comparator<Integer>,编译器将永远不会让它出现,因为那时,compare方法的方法签名将读取compare(Integer, Integer)而不是compare(Object, Object) {1}},意思是,如果一个对象不是Integer,它就永远不能传递给方法,这在编译时已经可以确保了

如果不这样做,您似乎会让ComparatorComparable感到困惑。 Comparable意味着某些东西具有“自然顺序”,就像Java所说的那样。整数是一个简单的例子,因为数字本身就是可量化的,所以比较两个数字是明确的(Double.NaN是一个特例,但那是另一回事)。因此,Integer有趣地实现Comparable<Integer>(但不是Comparable<Number>)。但是,当您想要比较非固有可量化的事物时,可能会出现这种情况。 Arun Sudhakaran在他的回答中使用了比较人的想法。他建议制作Person类工具Comparable<Person>,以便将其传递给TreeSet<Person>,但这对我来说似乎违反直觉,因为一个人本身并不是可量化的确切原因。您可能希望按工资对人员进行排序,但这种量化并不是一个人所固有的,只是针对您在这种情况下的特定意图。所以这将是Comparator<Person>的用例。您可以通过提供相应的Comparator<Person>按年龄,薪水,身高等来对人员进行排序,但Person本身并不具有可比性。

请注意,这两者不是互斥的 - 您还可以使用Comparable来比较实现Comparator的对象,Comparable的排序与equals(Object)对象的自然顺序不同。

现在当javadocs谈论比较“与equals一致”时,在此上下文中的“equals”总是指要比较的对象的Comparable方法 ,可能是对象实现Comparator本身或仅与Comparator的另一个对象进行比较。换句话说,如果比较方法(是equals(Object)或自然排序)认为两个对象相等,根据它们各自的equals(Object)方法也相等,反之亦然,这种比较方法是“与equals一致“这与Comparator的{​​{1}}方法无关,已由Mike Nakis指出(除非您想比较Comparator s。)

与equals不一致的自然顺序的一个例子是类BigDecimal的自然顺序。根据{{​​1}},代表BigDecimal2.0的两个2.00相等,但 等于它们的自然顺序(即BigDecimal.equals(Object))。