Overriding hashCode with overridden equals using equalsIgnoreCase to check equality

equals, hashcode, java, overriding

Solution

@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.

Problem

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?

Original source