Frage

I am doing some sample question from Enthuware simulator. Here is a sample question

public class GoodOne
{
   int theval;
   public int hashCode()
   {
      return theval%3;
   }
   public boolean equals(Object obj)
   {
      try{
      // 1 insert code here.
      }catch(Exception e) {
        return false;
      }
   }
}

The options are given below

  1. return true;
  2. return this == obj? true : (theval%3 == 0 && ((GoodOne)obj).theval%3==0) ? true :false;
  3. return theval%2 == 0? true :false;
  4. return ( (int)Math.random())*10%3 == 0? true :false;. Assume that Math.random() returns a double between 0.0 and 1.0 (not including 1.0).
  5. return false;

The correct answer opted by simulator is option-2 with this explanation given.

This means that the objects are considered equal if their theval % 3 is 0. Further, if the two objects are determined to be equal, then their hashcodes ( theval % 3) will always be same (zero). So it satisfies the requirements for hashCode and equals methods.

The rule to remember is: If the equals() method returns true, the hashCode() of the two objects MUST be the same. The reverse is desirable but not necessary.

Further, equals method must follow these rules: It should be reflexive: for any reference value x, x.equals(x) should return true. It should be symmetric: for any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true. It should be transitive: for any reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true. It should be consistent: for any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the object is modified. For any non-null reference value x, x.equals(null) should return false.

Option 1 is wrong because it will cause all the objects to be considered as equal and so the hashCode() would have to return same value for all the objects, which is not the case.

Option 2 is correct because hashCode() for all the multiples of 3 is 0. Therefore, if we return true in the equals() method for all the multiples of 3, the condition will be met. It also returns true if the object is compared with itself.

Option 3 is not correct because then 2 and 6 would be considered equal, but their hash codes will be different (2 and 0).

Option 4 is wrong because we cannot determine which objects will be considered equal.

I am unable to understand its explanation. Can someone elaborate why option-2 is correct in this sample. Thanks.

War es hilfreich?

Lösung

Option 2 is correct because hashCode() for all the multiples of 3 is 0. Therefore, if we return true in the equals() method for all the multiples of 3, the condition will be met. It also returns true if the object is compared with itself.

Well, I must say, this is a terrible explanation in favour of why that option works. Probably because, equals() and hashCode() method themselves have a terrible implementation. Currrently, the equals() method returns true, if the two objects have the same hashcodes (In a way). Let's take a look at that part again:

return this == obj? true : (theval%3 == 0 && ((GoodOne)obj).theval%3==0) ? true :false;

First of all, that expression can be improved to (although this is also not a good implementation):

return this == obj? true : theval % 3 == ((GoodOne)obj).theval % 3;

Now theVal % 3 is nothing but the hashcodes of that object. So, basically, this method is judging two objects equals based on their hashcodes. (Well, the hashCode() method isn't also an example of good implementation).

One big issue with the equals() method is, it doesn't handle null. My God. I suggest stop reading from that resource, what do you name it - Enthuware.

Having said that, this equals method would work fine in collaboration with the given hashcode (Provided, null is handled properly). How, let's consider the contract of equals method:

  • Reflexive: That is fine. a.theVal % 3 == a.theVal % 3.
  • Symmetric: This is also fine. For two objects a and b, if a.theVal % 3 == b.theVal % 3, then the reverse is also true.
  • Transitive - This is also fine. a.theVal % 3 = b.theVal % 3 and b.theVal % 3 == c.theVal % 3, implies that a.theVal % 3 == c.theVal % 3.
  • Consistent - Well, if a.theVal % 3 == b.theVal % 3, then that will be true always, provided neither of a.theval or b.theVal has changed.

Now comes the contract between equals() and hashCode():

  • If two objects are equals, then their hashCodes must be equal. That is certainly true here, since you are already judging objects equals based on hashcodes.

So, this explains how the 2nd option is fine here.


Something you should really keep in mind:

One general point, which is very important, to remember is, you should use the same set of object attributes to calculate the hashcodes that you use to judge the object equals, else the contract will break.

For e.g, if you have one more field in your class say - name, and your hashCode method is changed to:

theVal % 3 * name.hashCode();

but you didn't change the equals() method. Then if two objects have same theVal value, but different name, they will be equal as per the equals() method implementation, but their hashcodes will be different given the above implementation of hashCode().


Better equals() and hashCode()?

I said that, the given hashCode() and equals() method is not a very good implementation. Then what can be considered a somewhat good implementation. Well, I suggest you to go through Effective Java - Item 9, that really goes deep in this topic.

If you are using Eclipse IDE, then you can ask it to generate those two methods. Eclipse generates quite a good implementation of equals() and hashCode() methods.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top