Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding hashCode with overridden equals using equalsIgnoreCase to check equality

I've currently got an overridden equals(Object) that looks like this:

@Override
public boolean equals(Object o) {
    if (o == this) return true;
    if (! (o instanceof Player)) return false;
    Player p = (Player) o;
    return getFirstName().equalsIgnoreCase(p.getFirstName()) && 
            getLastName().equalsIgnoreCase(p.getLastName());
}

My hashCode() currently looks like this:

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + getFirstName().toLowerCase().hashCode();
    result = 31 * result + getLastName().toLowerCase().hashCode();
    return result;
}

My question is regarding my overridden hashCode() method. I know that I need hashCode() to return the same value for two objects if they are considered equal by the equals(Object) method. My gut tells me there is some case where this hashCode() will violate the contract.

Is there an acceptable way to use the equalsIgnoreCase(String) method in an overridden equals(Object) method and generate a hashcode that doesn't violate the contract?

like image 964
Jazzer Avatar asked Mar 26 '13 03:03

Jazzer


People also ask

How do you correctly override the hashCode () and equals () methods?

if a class overrides equals, it must override hashCode. when they are both overridden, equals and hashCode must use the same set of fields. if two objects are equal, then their hashCode values must be equal as well. if the object is immutable, then hashCode is a candidate for caching and lazy initialization.

What happens if we override equals method and override hashCode method?

You must override hashCode() in every class that overrides equals(). Failure to do so will result in a violation of the general contract for Object. hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.

Why we check hashCode () equality before equals () method?

If an object's hashcode is not the same as another object's hashcode, there is no reason to execute the equals() method: you just know the two objects are not the same. On the other hand, if the hashcode is the same, then you must execute the equals() method to determine whether the values and fields are the same.

When should I override hashCode and equals method to Bean?

Java recommends to override equals and hashCode method if equality is going to be defined by logical way or via some business logic and many classes in Java standard library does override it e.g. String overrides equals, whose implementation of equals() method return true if the content of two String objects is exactly ...


1 Answers

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + characterwiseCaseNormalize(getFirstName()).hashCode();
    result = 31 * result + characterwiseCaseNormalize(getLastName()).hashCode();
    return result;
}

private static String characterwiseCaseNormalize(String s) {
    StringBuilder sb = new StringBuilder(s);
    for(int i = 0; i < sb.length(); i++) {
        sb.setCharAt(i,Character.toLowerCase(Character.toUpperCase(sb.charAt(i))));
    }
    return sb.toString();
}

This hashCode will be consistent with an equals defined using equalsIgnoreCase. In principle, according to the contract of equalsIgnoreCase, this seems to rely on it being the case that

Character.toLowerCase(Character.toUpperCase(c1))==Character.toLowerCase(Character.toUpperCase(c2))

whenever

Character.toLowerCase(c1)==Character.toLowerCase(c2).  

I don't have proof that that is true, but the OpenJDK implementation of equalsIgnoreCase actually does it consistently with this method; it checks whether corresponding characters are equals, then whether their upper case versions are equals, then whether the lower case versions of the upper case versions are equal.

like image 160
Robert Tupelo-Schneck Avatar answered Nov 15 '22 21:11

Robert Tupelo-Schneck