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?
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.
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.
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.
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 ...
@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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With