简体   繁体   English

根据 TreeSet,两个对象显示为相等,但 Queue 将它们显示为不相等

[英]As per TreeSet two objects are shown equal but Queue shows them as unequal

I have the follwing Person Class -我有以下 Person 类-

Person.java - 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;
    }
}

Now, I have the following TreeClass instance -现在,我有以下 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")
        ));

Upon printing -打印时——

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

So clearly, the two Person instances - new Person(4, "Amrita") and new Person(4, "Amrita") are equal.很明显,两个 Person 实例 - new Person(4, "Amrita")new Person(4, "Amrita")是相等的。

Now, I have the following Queue code.现在,我有以下队列代码。 Since, Queue is a subinterface of Collection interface it implements all methods of Collection interface.由于 Queue 是 Collection 接口的子接口,它实现了 Collection 接口的所有方法。 So -所以 -

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

Ouput -输出 -

    false
    false

Thus it says that new Person(4, "Amrita") element of Queue and Object new Person(4, "Amrita") are unequal.因此,它表示 Queue 的new Person(4, "Amrita")元素和 Object new Person(4, "Amrita")不相等。

How is this possible?这怎么可能?

You need to override @equals method in the class Person .您需要覆盖Person类中的@equals方法。 See documentation .请参阅文档

Additionally I see that Person implemented Comparable (TreeSet uses compareTo for equality).此外,我看到Person实现了Comparable (TreeSet 使用compareTo来表示相等)。 Whenever you @override compareTo() , it is highly recommended to @overide @equals .每当您@override compareTo()时,强烈建议您使用@overide @equals

From Collections Documentation :集合文档

It is strongly recommended (though not required) that natural orderings be consistent with equals.强烈建议(尽管不是必需的)自然排序与 equals 一致。 This is so because sorted sets (and sorted maps) without explicit comparators behave "strangely" when they are used with elements (or keys) whose natural ordering is inconsistent with equals.之所以如此,是因为没有显式比较器的排序集(和排序映射)在与自然顺序与等于不一致的元素(或键)一起使用时表现“奇怪”。

To cut the long story short, since you are using TreeSet as well as other collections (List), you need to @override all three - compareTo, equals and hashCode长话短说,由于您使用 TreeSet 以及其他集合(列表),您需要 @override 所有三个 - compareTo, equals and hashCode

PS: Whenever you override @equals - please @override hashCode too. PS:每当你覆盖@equals - 请@override hashCode See this .看到这个

You can generate these methods inside IDE (Intellij / Eclipse/ VsCode etc) automatically.您可以在 IDE(Intellij/Eclipse/VsCode 等)中自动生成这些方法。 Eg it would be something like this:例如,它会是这样的:

@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 exclusively uses the comparator to determine equality. TreeSet专门使用比较器来确定相等性。 Any 2 entries such that comparator.compare(a, b) returns 0 are deemed equal. comparator.compare(a, b)返回 0 的任何 2 个条目都被视为相等。 Neither equals nor hashCode are invoked at all.根本不调用equalshashCode

Most other collections exclusively use either just equals , or a combination of equals and hashCode to determine this.大多数其他集合使用equalsequalshashCode的组合来确定这一点。

Thus, if you write a class whose equals, hashCode, and compare methods are in disagreement, you can create the situation you described: Where TreeSet considers them equal but eg HashSet does not.因此,如果您编写的类的 equals、hashCode 和 compare 方法不一致,您可以创建您所描述的情况:TreeSet 认为它们相等但例如 HashSet 不相等。

The solution is to properly apply the rules as laid out in the javadocs of the various java.util classes:解决方案是正确应用各种java.util类的 javadocs 中列出的规则:

  • a.equals(b) ? a. a.equals(b) Then, b.equals(a) must also hold, regardless of the types.然后,无论类型如何, b.equals(a)也必须成立。
  • a.equals(a) must be true. a.equals(a)必须为真。
  • a.equals(anything) cannot throw any exceptions, except the forced NPE if a is null . a.equals(anything)不能抛出任何异常,除非anull时强制 NPE。 Same for a.hashCode() .a.hashCode()相同。
  • a.equals(null) must be false. a.equals(null)必须为假。
  • a.equals(b) and b.equals(c) ? a.equals(b)b.equals(c) Then a.equals(c) must also be true.那么a.equals(c)也必须为真。
  • a.equals(b) ? a. a.equals(b) Then a.hashCode() == b.hashCode() must also be true.那么a.hashCode() == b.hashCode()也必须为真。 The reverse does not hold (equal hashcodes? That doesn't imply equal objects).反过来不成立(相等的哈希码?这并不意味着相等的对象)。
  • a.compare(b) is below 0? a.compare(b)低于 0? Then b.compare(a) must be above 0.那么b.compare(a)必须大于 0。
  • a.compare(a) must be 0. a.compare(a)必须为 0。
  • a.compare(b) == 0 must match with a.equals(b) If one is true the other must be true and vice versa. a.compare(b) == 0必须与a.equals(b)匹配,如果一个为真,则另一个必须为真,反之亦然。
  • a.compare(b) < 0 and b.compare(c) < 0 ? a.compare(b) < 0b.compare(c) < 0 Then a.compare(c) must also be below 0. Same for above 0.那么a.compare(c)也必须低于 0。高于 0 也是如此。
  • Unlike equals and hashCode , compare is allowed to throw a number of exceptions.equalshashCode不同, compare允许抛出许多异常。

It's a lot of rules.是很多规则。 They are easy to understand (bordering on the obvious), and yet they are quite hard to properly apply especially if you involve the idea that java has a type hierarchy.它们很容易理解(几乎是显而易见的),但它们很难正确应用,尤其是当您涉及到 java 具有类型层次结构的想法时。

Trivial example:简单的例子:

ArrayList has an equals impl that will check if the provided object is a list of any kind, and if it is, it'll just do an elem-by-elem comparison. ArrayList有一个 equals impl,它将检查提供的对象是否是任何类型的列表,如果是,它将只进行逐个元素的比较。

This means you CANNOT write a class that implements List and add equality-affecting properties of ANY sort, period .这意味着您不能编写实现List的类并添加任何排序的影响相等的属性 period

For example, this class:例如,这个类:

class ColouredList<T> extends ArrayList<T> {
  private Color color;

  public ColouredList(Color color) {
    this.color = color;
  }

  // and so on
}

CANNOT BE WRITTEN - unless you disregard colour entirely, and eg an empty blue list is deemed equal to an empty red one.不能写——除非你完全不考虑颜色,例如一个空的蓝色列表被认为等于一个空的红色列表。 Because emptyPlainArrayList.equals(blueEmptyList) is true and you can't make that false (as you do not control the equals impl of ArrayList), and so is emptyPlainArrayList.equals(redEmptyList) , therefore, blueEmptyList.equals(redEmptyList) also has to be true.因为emptyPlainArrayList.equals(blueEmptyList)是真的,你不能把它设为假(因为你不控制 ArrayList 的 equals impl emptyPlainArrayList.equals(redEmptyList) ,emptyPlainArrayList.equals(redEmptyList) 也是如此,因此blueEmptyList.equals(redEmptyList)也有是真实的。

This rule is a logical consequence of the ruleset, and yet, not obvious and in fact a somewhat common mistake to think you can do that.这条规则是规则集的逻辑结果,但并不明显,实际上认为你可以做到这一点是一个有点常见的错误。

Hence, easy, almost obvious rules that combine to make a system that is much more complex than you'd think.因此,简单的、几乎显而易见的规则结合起来形成了一个比你想象的要复杂得多的系统。

You violated one of the rules here;您违反了此处的一项规则; given what you pasted, seems like a simple one: You added a natural comparison order to your class but failed to implement hashCode and equals.鉴于您粘贴的内容,看起来很简单:您向您的类添加了自然比较顺序,但未能实现 hashCode 和 equals。 You must implement these methods, in order to follow the rule that a.equals(b) and a.compare(b) == 0 have to hold (and you have to implement hashCode to follow the rule "if a.equals(b) then you have to ensure a.hashCode() == b.hashCode() .您必须实现这些方法,以遵循a.equals(b)a.compare(b) == 0必须遵守的规则(并且您必须实现hashCode以遵循规则“如果a.equals(b)那么你必须确保a.hashCode() == b.hashCode()

I suggest Project Lombok or your IDE's "generate equals and hashcode" option.我建议Project Lombok或您的 IDE 的“生成等于和哈希码”选项。 These methods, too, can be a bit tricky to write properly.这些方法也可能有点难以正确编写。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM