根据 TreeSet,两个对象显示为相等,但 Queue 显示它们不相等
As per TreeSet two objects are shown equal but Queue shows them as unequal
我有以下人 Class -
Person.java
-
public class Person implements Comparable<Person> {
private int id;
private String name;
Person(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Person: Id = " + id + ", Name = " + name;
}
@Override
public int compareTo(Person person) {
int myReturn = 0;
int minLength = 0;
int i = 0;
boolean equal = false;
if (id > person.id) {
myReturn = 1;
} else if (id < person.id) {
myReturn = -1;
} else {
if (name.length() > person.name.length()) {
minLength = person.name.length();
} else if (name.length() < person.name.length()) {
minLength = name.length();
} else {
equal = true;
minLength = name.length();
}
for (i = 0; i < minLength; i++) {
if (name.charAt(i) > person.name.charAt(i)) {
myReturn = 1;
break;
} else if (name.charAt(i) < person.name.charAt(i)) {
myReturn = -1;
break;
} else {
continue;
}
}
if (i == minLength) {
if (equal) {
myReturn = 0;
} else if (name.length() > person.name.length()) {
myReturn = 1;
} else {
myReturn = -1;
}
}
}
return myReturn;
}
}
现在,我有以下 TreeClass 实例 -
TreeSet<Person> treeSet = new TreeSet<>(List.of(
new Person(4, "Amrita"),
new Person(4, "Amrita"),
new Person(9, "Sunita"),
new Person(12, "Nisha"),
new Person(9, "Sunit"),
new Person(9, "Sunitaa")
));
打印时 -
Person: Id = 4, Name = Amrita
Person: Id = 9, Name = Sunit
Person: Id = 9, Name = Sunita
Person: Id = 9, Name = Sunitaa
Person: Id = 12, Name = Nisha
很明显,两个 Person 实例 - new Person(4, "Amrita")
和 new Person(4, "Amrita")
是相等的。
现在,我有以下队列代码。因为,Queue 是 Collection 接口的子接口,它实现了 Collection 接口的所有方法。所以-
Queue<Person> queue3 = new LinkedList<>(List.of(
new Person(4, "Amrita"),
new Person(2, "Suhana"),
new Person(7, "Neha")
));
Person person1 = new Person(4, "Amrita");
Person person2 = new Person(9, "Sunita");
System.out.println(queue3.contains(person1));
System.out.println(queue3.contains(person2));
输出-
false
false
因此它说 new Person(4, "Amrita")
元素的队列和对象 new Person(4, "Amrita")
是不相等的。
这怎么可能?
您需要覆盖 class Person
中的 @equals
方法。参见 documentation。
此外,我看到 Person
实现了 Comparable
(TreeSet 使用 compareTo
来表示相等)。每当你@override
compareTo()
,强烈推荐@overide
@equals
.
强烈建议(虽然不是必需的)自然排序与等号一致。之所以如此,是因为没有显式比较器的排序集(和排序映射)在与自然顺序与 equals 不一致的元素(或键)一起使用时表现得“奇怪”。
长话短说,因为您正在使用 TreeSet 以及其他集合 (List),您需要 @override 所有三个 - compareTo, equals and hashCode
PS:无论何时覆盖 @equals
- 请也 @override
hashCode
。参见 this。
您可以在 IDE(Intellij / Eclipse/ VsCode 等)中自动生成这些方法。例如。它会是这样的:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
TreeSet
独占使用比较器判断是否相等。 comparator.compare(a, b)
returns 0 的任何 2 个条目被视为相等。 equals
和 hashCode
都没有被调用。
大多数其他集合 独占 仅使用 equals
或 equals
和 hashCode
的组合来确定这一点。
因此,如果你写了一个class,它的equals、hashCode和compare方法不一致,你可以创造你所描述的情况:TreeSet认为它们相等但是例如HashSet 没有。
解决方案是正确应用各种 java.util
classes:java文档中列出的规则:
a.equals(b)
?然后,b.equals(a)
也必须成立,无论类型如何。
a.equals(a)
必须为真。
a.equals(anything)
不能抛出任何异常,除非 a
是 null
时的强制 NPE。 a.hashCode()
. 相同
a.equals(null)
一定是假的。
a.equals(b)
和 b.equals(c)
?那么a.equals(c)
也一定为真。
a.equals(b)
?那么 a.hashCode() == b.hashCode()
也一定为真。反过来不成立(相等的哈希码?这并不意味着相等的对象)。
a.compare(b)
低于0?那么b.compare(a)
一定在0以上。
a.compare(a)
必须为 0。
a.compare(b) == 0
必须与 a.equals(b)
匹配,如果一个为真,另一个必须为真,反之亦然。
a.compare(b) < 0
和 b.compare(c) < 0
?然后 a.compare(c)
也必须小于 0。与大于 0 相同。
- 与
equals
和 hashCode
不同,compare
允许抛出多个异常。
很多规则。它们很容易理解(近乎显而易见),但很难正确应用,尤其是当您涉及 java 具有类型层次结构的想法时。
简单的例子:
ArrayList
有一个 equals impl,它将检查提供的对象是否是任何类型的列表,如果是,它只会进行 elem-by-elem 比较。
这意味着您不能编写实现 List
的 class 并添加任何类型的 equality-affecting 属性,期间 .
比如这个class:
class ColouredList<T> extends ArrayList<T> {
private Color color;
public ColouredList(Color color) {
this.color = color;
}
// and so on
}
无法写入 - 除非你完全无视颜色,例如一个空的蓝色列表被认为等于一个空的红色列表。因为 emptyPlainArrayList.equals(blueEmptyList)
是真的,你不能让它成为假(因为你不控制 ArrayList 的 equals impl),所以 emptyPlainArrayList.equals(redEmptyList)
,因此,blueEmptyList.equals(redEmptyList)
也必须是真的.
此规则是规则集的逻辑结果,但并不明显,实际上认为您可以这样做是一个有点常见的错误。
因此,简单、几乎显而易见的规则组合在一起,构成了一个比您想象的要复杂得多的系统。
您违反了其中一项规则;根据您粘贴的内容,看起来很简单:您向 class 添加了自然比较顺序,但未能实现 hashCode 和 equals。您必须实施这些方法,以遵循 a.equals(b)
和 a.compare(b) == 0
必须成立的规则(并且您必须实施 hashCode
以遵循“如果 a.equals(b)
然后你必须确保 a.hashCode() == b.hashCode()
.
我建议 Project Lombok 或您的 IDE 的“生成等号和哈希码”选项。这些方法也可能有点难以正确编写。
我有以下人 Class -
Person.java
-
public class Person implements Comparable<Person> {
private int id;
private String name;
Person(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Person: Id = " + id + ", Name = " + name;
}
@Override
public int compareTo(Person person) {
int myReturn = 0;
int minLength = 0;
int i = 0;
boolean equal = false;
if (id > person.id) {
myReturn = 1;
} else if (id < person.id) {
myReturn = -1;
} else {
if (name.length() > person.name.length()) {
minLength = person.name.length();
} else if (name.length() < person.name.length()) {
minLength = name.length();
} else {
equal = true;
minLength = name.length();
}
for (i = 0; i < minLength; i++) {
if (name.charAt(i) > person.name.charAt(i)) {
myReturn = 1;
break;
} else if (name.charAt(i) < person.name.charAt(i)) {
myReturn = -1;
break;
} else {
continue;
}
}
if (i == minLength) {
if (equal) {
myReturn = 0;
} else if (name.length() > person.name.length()) {
myReturn = 1;
} else {
myReturn = -1;
}
}
}
return myReturn;
}
}
现在,我有以下 TreeClass 实例 -
TreeSet<Person> treeSet = new TreeSet<>(List.of(
new Person(4, "Amrita"),
new Person(4, "Amrita"),
new Person(9, "Sunita"),
new Person(12, "Nisha"),
new Person(9, "Sunit"),
new Person(9, "Sunitaa")
));
打印时 -
Person: Id = 4, Name = Amrita
Person: Id = 9, Name = Sunit
Person: Id = 9, Name = Sunita
Person: Id = 9, Name = Sunitaa
Person: Id = 12, Name = Nisha
很明显,两个 Person 实例 - new Person(4, "Amrita")
和 new Person(4, "Amrita")
是相等的。
现在,我有以下队列代码。因为,Queue 是 Collection 接口的子接口,它实现了 Collection 接口的所有方法。所以-
Queue<Person> queue3 = new LinkedList<>(List.of(
new Person(4, "Amrita"),
new Person(2, "Suhana"),
new Person(7, "Neha")
));
Person person1 = new Person(4, "Amrita");
Person person2 = new Person(9, "Sunita");
System.out.println(queue3.contains(person1));
System.out.println(queue3.contains(person2));
输出-
false
false
因此它说 new Person(4, "Amrita")
元素的队列和对象 new Person(4, "Amrita")
是不相等的。
这怎么可能?
您需要覆盖 class Person
中的 @equals
方法。参见 documentation。
此外,我看到 Person
实现了 Comparable
(TreeSet 使用 compareTo
来表示相等)。每当你@override
compareTo()
,强烈推荐@overide
@equals
.
强烈建议(虽然不是必需的)自然排序与等号一致。之所以如此,是因为没有显式比较器的排序集(和排序映射)在与自然顺序与 equals 不一致的元素(或键)一起使用时表现得“奇怪”。
长话短说,因为您正在使用 TreeSet 以及其他集合 (List),您需要 @override 所有三个 - compareTo, equals and hashCode
PS:无论何时覆盖 @equals
- 请也 @override
hashCode
。参见 this。
您可以在 IDE(Intellij / Eclipse/ VsCode 等)中自动生成这些方法。例如。它会是这样的:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
TreeSet
独占使用比较器判断是否相等。 comparator.compare(a, b)
returns 0 的任何 2 个条目被视为相等。 equals
和 hashCode
都没有被调用。
大多数其他集合 独占 仅使用 equals
或 equals
和 hashCode
的组合来确定这一点。
因此,如果你写了一个class,它的equals、hashCode和compare方法不一致,你可以创造你所描述的情况:TreeSet认为它们相等但是例如HashSet 没有。
解决方案是正确应用各种 java.util
classes:java文档中列出的规则:
a.equals(b)
?然后,b.equals(a)
也必须成立,无论类型如何。a.equals(a)
必须为真。a.equals(anything)
不能抛出任何异常,除非a
是null
时的强制 NPE。a.hashCode()
. 相同
a.equals(null)
一定是假的。a.equals(b)
和b.equals(c)
?那么a.equals(c)
也一定为真。a.equals(b)
?那么a.hashCode() == b.hashCode()
也一定为真。反过来不成立(相等的哈希码?这并不意味着相等的对象)。a.compare(b)
低于0?那么b.compare(a)
一定在0以上。a.compare(a)
必须为 0。a.compare(b) == 0
必须与a.equals(b)
匹配,如果一个为真,另一个必须为真,反之亦然。a.compare(b) < 0
和b.compare(c) < 0
?然后a.compare(c)
也必须小于 0。与大于 0 相同。- 与
equals
和hashCode
不同,compare
允许抛出多个异常。
很多规则。它们很容易理解(近乎显而易见),但很难正确应用,尤其是当您涉及 java 具有类型层次结构的想法时。
简单的例子:
ArrayList
有一个 equals impl,它将检查提供的对象是否是任何类型的列表,如果是,它只会进行 elem-by-elem 比较。
这意味着您不能编写实现 List
的 class 并添加任何类型的 equality-affecting 属性,期间 .
比如这个class:
class ColouredList<T> extends ArrayList<T> {
private Color color;
public ColouredList(Color color) {
this.color = color;
}
// and so on
}
无法写入 - 除非你完全无视颜色,例如一个空的蓝色列表被认为等于一个空的红色列表。因为 emptyPlainArrayList.equals(blueEmptyList)
是真的,你不能让它成为假(因为你不控制 ArrayList 的 equals impl),所以 emptyPlainArrayList.equals(redEmptyList)
,因此,blueEmptyList.equals(redEmptyList)
也必须是真的.
此规则是规则集的逻辑结果,但并不明显,实际上认为您可以这样做是一个有点常见的错误。
因此,简单、几乎显而易见的规则组合在一起,构成了一个比您想象的要复杂得多的系统。
您违反了其中一项规则;根据您粘贴的内容,看起来很简单:您向 class 添加了自然比较顺序,但未能实现 hashCode 和 equals。您必须实施这些方法,以遵循 a.equals(b)
和 a.compare(b) == 0
必须成立的规则(并且您必须实施 hashCode
以遵循“如果 a.equals(b)
然后你必须确保 a.hashCode() == b.hashCode()
.
我建议 Project Lombok 或您的 IDE 的“生成等号和哈希码”选项。这些方法也可能有点难以正确编写。