实际上我正在阅读其中提到的一个教程,当我们需要实现比较器接口时,我们可以覆盖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方法的功能性概念,请
答案 0 :(得分:7)
在equals()
上实现Comparator
方法允许您指示一个比较器提供与另一个比较器相同的顺序。它与元素的排序方式无关。它是一种非常先进且极少需要的功能。您极不可能遇到实际调用比较器equals()
方法的情况。我建议你忽略它。
答案 1 :(得分:2)
equals()
方法Comparator
和Comparator
需要等于一致性,因为如果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
有两个属性:weight
和price
,并希望将此类型的对象放入已排序的集合TreeSet
public class Apple {
private int weight;
private int cost;
现在,您希望如何在集合内对这些苹果进行排序 - 是否要按weight
或price
对其进行排序?编译器应如何决定?
提供适当的Comparator
或Comparable
可让您传递意图。
让我们尝试将对象添加到集合中。
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());
再次收集将按成本进行分类,相应地提供给比较器。
因此,我们需要提供Comparator
或Comparable
才能将它们存储在集合中,尤其是在TreeSet
中。
现在关于您的问题:Comparator
和equals()
方法之间没有关联。 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)
我不明白为什么要关联Comparator
和equals()
方法。像interfaces
和Comparable
这样的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);
,原因是因为集合TreeSet
对Peron
类的哪个属性应该这样做感到困惑排序。这就是interfaces
Comparable
和Comparator
具有重要意义的地方。
将此代码添加到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
,它就永远不能传递给方法,这在编译时已经可以确保了
如果不这样做,您似乎会让Comparator
和Comparable
感到困惑。 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}},代表BigDecimal
和2.0
的两个2.00
将不相等,但将 等于它们的自然顺序(即BigDecimal.equals(Object)
)。