Maintaining the hashCode contract for a specific condition is () depending on two integers

I have a base class with a structure:

class Employee {
int eId;
String eName;

Employee(int id, String name) {
    this.eId= id;
    this.eName= name;
}

      

The equality conditions are such that equals()

must return true if one of the following conditions is true:

  • eId

    - the same.
  • eName

    - the same.
  • The lengths are the eName

    same.

I had no problem with overriding equals()

, however, in order to preserve the contract of the hashcode, I have to override hashCode()

. So hashCode must depend on eId

and eName.length()

(if eName

equal, their lengths will also be equal). Thus, there are four cases:

Employee e1 = new Employee(4, "John");
Employee e2 = new Employee(3, "Jane");
Employee e3 = new Employee(4, "Jim");
Employee e4 = new Employee(7, "Random");

      

hashCode()

must return the same value e1

, e2

and e3

another meaning for e4

. I cannot think of any logic to satisfy this requirement.

Here's the exact problem:

Create a class (with parameter name, id, etc.). Show that if 2 objects of this class are to be compared, they must return true in any of the following cases:

and. The identifier for both values ​​is the same.

Q. The name of both is the same.

C. The length of the names is the same.

Make sure the HashCode contract must not be violated.

+3


source to share


3 answers


You are in trouble because your concept of equality is incompatible. In particular, it is not transitive, which requires a contract .equals()

.

It is transitive: for any non-null reference values x

, y

and z

if it x.equals(y)

returns true

and y.equals(z)

returns true

, then it x.equals(z)

must return true

.

In your definition, e1

equal e2

and e3

but e2

not equal e3

. This is incompatible with Java's notion of equality. This is why you have found it difficult to determine a reasonable implementation .hashCode()

.



However, what you can do is define a custom Comparator

(or Ordering

if you are using Guava). In most use cases (like sorting, searching, or filtering) you should use a separate instance Comparator

, just like the method .equals()

. You are effectively trying to define equivalent objects, not equal objects.

If you can't use a separate one Comparator

for whatever reason, your object Employee

will be fundamentally inconsistent and problematic even if you have to implement "workable" .hashCode()

.

+7


source


I think you cannot write sequential hashCode

in your case, because yours equals

violates the contract of the Object.equals

method, namely transitivity:

It is transitive: for any non-null reference values x

, y

and z

if it x.equals(y)

returns true

and y.equals(z)

returns true

, then it x.equals(z)

should return true

.

Let's assume you have this code:

Employee a = new Employee(1, "John");
Employee b = new Employee(1, "James");
Employee c = new Employee(2, "James");

      



In this case, with your operation equals a.equals(b)

and b.equals(c)

but not a.equals(c)

.

I would suggest revisiting the implementation equals

. Yours probably Employee

has either eId

or eName

, so it would be better to add a field boolean

that says whether to use eId

or eName

. So you can easily implement equals

and hashCode

. Thus, the implementation can be like this (if for simplicity eName

it cannot be null

):

class Employee {
    boolean useName;
    int eId = 0;
    String eName;

    Employee(int id) {
        this.eId = id;
        this.useName = false;
    }

    Employee(String name) {
        this.eName = name;
        this.useName = true;
    }

    @Override
    public int hashCode() {
        return useName ? eName.length() * 1337 : eId * 7331;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
        if (useName != other.useName)
            return false;
        if (useName) {
            if (eName.length() != other.eName.length())
                return false;
        } else {
            if (eId != other.eId)
                return false;
        }
        return true;
    }
}

      

+4


source


If you are using Java 7 I think this is easy with java.util.Objects:

@Override
public int hashCode() {
    return Objects.hash(eld,eName.length());
}

      

Or you can consider Guava , it has the same method incom.google.common.base.Objects

0


source







All Articles