1

I want to compare two objects. However, it tells me they do not match when I run it, even though they do. Please let me know what I am doing wrong, thank you. Here is my code:

      Player p1 = new Player("Mo", "Food", 200.0,0.0);
      Player p2 = new Player("Mo", "Food", 200.0,0.0);
      
      System.out.println(p1.equals(p2)); // -- false
aran
  • 10,978
  • 5
  • 39
  • 69
mojava
  • 35
  • 4
  • 5
    Does your Player class override the "equals" method? By default, equals does not compare a custom class' inner state. So if you don't override the equals method in your Player class, then equals will be true ONLY if p and p2 are literally the same – Martín Zaragoza Feb 27 '21 at 00:50

2 Answers2

8

Your equals() is calling the default Object class's method, which compares the Objects's identity*, that is, p1==p2, not its contents. This is the default equals from Object:

public boolean equals(Object obj) {
    return (this == obj);
}

If you want to define your logic in order to decide if two Players are equal, you need to override:

  • equals() method
  • hashCode() method to honor the equals-hash contract

class Player {
    private String firstName;
    private String lastName;
    private double score;
    private double rating;

    // ...

    @Override
    public int hashCode() {
        return Objects.hash(firstName, lastName, score, rating);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null || getClass() != obj.getClass())
            return false;
        Player other = (Player) obj;
        return Objects.equals(firstName, other.firstName) && Objects.equals(lastName, other.lastName)
                && score == other.score && rating == other.rating;
    }

    // ...
}

Java SE 16

Java 16 Records feature helps you get rid of all this boilerplate code. Most of the ceremonial code mentioned above is automatically available to you with just the following declaration:

record Point(String firstName, String lastName, double score, double rating) { }

If you want to work with Collections:

  • Implement Comparable -- this won't let you directly compare, but useful for collections
  • Implement Comparator

If you want to work with Collections, in order to be able to sort, for example, you could implement the Comparable interface. This avoids breaking the equals and hashcode contract, which is pretty difficult to achieve manually.

As Holger comments (he's giving me some lessons today) use this when there's a numeric based ordering involved.

If not, you can use the Comparator interface.

In this example I'm using Comparable:

class Player implements Comparable<Player> 
{
   @Override
   public int compareTo(Player p2)  
   {
      /* Allows returning : (0)-> this==p2 | (1)-> this>p2 | (-1)-> this<p2
        Anyway, the meaning of 1 and -1 is up to you.
        if (this.name.equals(p2.name) && ...)
           return 0;
       else if ... 
            return 1;
       return -1;     */
   }
} 

* Thanks to Henry Twist for pointing this out.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
aran
  • 10,978
  • 5
  • 39
  • 69
  • As far as I am aware the default Java implementation of equals is reference equality not hashcode? If you look at the source we just have return (this == obj); – Henry Twist Feb 27 '21 at 01:12
  • 1
    That's the default Object equals method. Subclasses override it, for example, String. Without it, `String a = "name"; String b="name"; a.equals(b)` would never be true. And that's exactly why strings should never be compared with `==`, which is an usual mistake – aran Feb 27 '21 at 01:17
  • 1
    @HenryTwist Anyway, you have a point with the hashCode comment. I updated the answer to avoid misleading this. You were right with that-- The point is that there's a contract between hashcode and equals, which shouldn't be broken. If two objects are equals as per ==, then their hashCode must be equal. The usual overriding of equals consists in comparing hashCodes – aran Feb 27 '21 at 01:22
  • 2
    To avoid breaking the equals and hashcode contract, you have to override the `hashCode` method. This is not even remotely related to implementing `Comparable`. In fact, you shouldn’t implement `Comparable` for things that don’t have a *natural order*. You can easily sort such objects using a dedicated `Comparator`, e.g. `Comparator.comparing(Person::getName)`. There are different ways to sort such objects and you can have different `Comparator`s. And, by the way, instead of `if(condition) return true; [else] return false;` you can simply write `return condition;`. – Holger Feb 27 '21 at 10:50
4

with the new record class type this is finally addressed.

    record Player(String name, String item, double val1, double val2){}

    Player p1 = new Player("Mo", "Food", 200.0, 0.0);
    Player p2 = new Player("Mo", "Food", 200.0, 0.0);

    System.out.println("equals:"+p1.equals(p2));
    System.out.println("==:"+(p1==p2));

will print

   equals:true
   ==:false
karakfa
  • 66,216
  • 7
  • 41
  • 56