我目前有一个被覆盖的equals(Object)
看起来像这样:
@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());
}
我的hashCode()
目前看起来像这样:
@Override
public int hashCode() {
int result = 17;
result = 31 * result + getFirstName().toLowerCase().hashCode();
result = 31 * result + getLastName().toLowerCase().hashCode();
return result;
}
我的问题是关于我的重写hashCode()方法。我知道我需要hashCode()为两个对象返回相同的值,如果它们被equals(Object)方法认为是相等的。我的直觉告诉我有一些的情况,这个hashCode()将违反合同。
是否有一种可接受的方法在重写的equals(Object)方法中使用equalsIgnoreCase(String)方法并生成一个不违反合同的哈希码?
答案 0 :(得分:4)
@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();
}
此hashCode
与使用equals
定义的equalsIgnoreCase
一致。原则上,根据equalsIgnoreCase
的合同,这似乎依赖于
Character.toLowerCase(Character.toUpperCase(c1))==Character.toLowerCase(Character.toUpperCase(c2))
每当
Character.toLowerCase(c1)==Character.toLowerCase(c2).
我没有证明这是真的,但OpenJDK implementation of equalsIgnoreCase实际上与此方法一致;它检查相应的字符是否相等,然后它们的大写版本是否相等,那么大写版本的小写版本是否相等。
答案 1 :(得分:1)
你是对的。我们可以循环遍历所有one-char字符串,找到s1,s2
对s1.equalsIgnoreCase(s2) && !s1.toLowerCase().equals(s2.toLowerCase())
。有很多对。例如
s1=0049 'LATIN CAPITAL LETTER I'
s2=0131 'LATIN SMALL LETTER DOTLESS I'
s1.lowercase = 0069 'LATIN SMALL LETTER I'
s2.lowercase = 0131 itself
它还取决于语言环境:对于s1,土耳其语和阿塞拜疆语使用U + 0131表示小写(参见http://www.fileformat.info/info/unicode/char/0049/index.htm)
答案 2 :(得分:1)
你担心是对的。 Read the contract for equalsIgnoreCase
如果至少满足下列条件之一,则两个字符c1和c2被视为相同的忽略大小写:
所以,如果在转换为 upper 的情况下有一个相同的字符而不是相反的情况,那么你将遇到麻烦。
让我们以德语字符ß为例,转换为大写字母后变为two character sequence SS
。这意味着字符串“ß”和“SS”是“equalsIgnoreCase”,但在转换为小写时不会有相同的表示形式!
所以你的方法在这里被打破了。不幸的是,我不确定你是否能够在这里设计一个充分表达你需要的hashCode。
答案 3 :(得分:1)
在编写与hashCode()
一致的equals()
方面,您应该使用基于Character
的案例映射,或基于String
的案例映射都。在我的另一个答案中,我展示了如何使用基于hashCode()
的案例映射来编写Character
;但还有另一个解决方案,即改变equals()
而不是使用基于String
的案例映射。 (请注意,String.equalsIgnoreCase()
使用基于Character
的案例映射。)
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (! (o instanceof Player)) return false;
Player p = (Player) o;
return getFirstName().toLowerCase().equals(p.getFirstName().toLowerCase()) &&
getLastName().toLowerCase().equals(p.getLastName().toLowerCase());
}