在第3章第8项:
public final class CaseInsensitiveString {
private final String s;
public CaseInsensitiveString(String s) {
if (s == null)
throw new NullPointerException();
this.s = s;
}
@Override public boolean equals(Object o) {
return o instanceof CaseInsensitiveString &&
((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}
// remainder omitted
}
在描述了equals()
方法的问题后,他继续在比较字段的上下文中讨论这个类。
对于某些类,例如上面的CaseInsensitiveString,字段比较比简单的相等测试更复杂。如果是这种情况,您可能希望存储该字段的规范形式,因此
equals()
方法可以对这些规范形式进行廉价的精确比较,而不是更昂贵的不精确比较。这种技术最适合不可变的类;如果对象可以更改,则必须使规范形式保持最新。
所以我的问题(我仔细检查了'规范'意味着什么):Bloch在谈论什么?规范形式是什么?我已经准备好被告知答案很简单(大概是他的编辑会告诉他增加更多),但我想看到其他人这么说。
他还在下一个项目9中提到hashCode()
的相同内容。
为了在上下文中给出它,他还讨论了equals()
的{{1}}方法的错误版本:
CaseInsensitiveString
答案 0 :(得分:6)
您应该为其添加另一个final
字段并存储值s.toUpperCase()
。
此新字段将是规范表示 s
字段。方法equals()
的新实现(参见下面的代码)将更便宜。这种方法仅适用于不可变类。
如果您覆盖hashCode()
,则不应忘记覆盖equals()
。
public final class CaseInsensitiveString {
private final String s;
private final String sForEquals; //field added for simplifier equals method
public CaseInsensitiveString(String s) {
if (s == null) {
throw new IllegalArgumentException(); //NullPointerException() - bad practice
}
this.s = s;
this.sForEquals = s.toUpperCase();
}
@Override
public boolean equals(Object o) {
return o instanceof CaseInsensitiveString &&
((CaseInsensitiveString) o).sForEquals.equals(this.sForEquals);
}
@Override
public int hashCode(){
return sForEquals.hashCode();
}
// remainder omitted
}
答案 1 :(得分:2)
术语 canonical 有一些不同的用法。它指的是具有多个表示的值(或者可能是几个相等的变化值)。然后经常选择一个特定的表示(或值)作为规范表示。
示例:整数集:规范{2,3,5} = {3,5,2} = {2,2,5,3} = ....
对于普通的java String也存在问题。 Unicode中的相同文本可以用不同的方式表示:ĉ
或者作为一个代码点"\u0109"
SMALL-LETTER-C-WITH-CIRCUMFLEX,或者作为两个代码点c
SMALL-LETTER-C和零宽度^
COMBINED-DIACRITICAL-MARK-CIRCUMFLEX("\u0063\u0302"
)。
因此,在某些情况下,即使是普通的字符串也应该规范化:
String s = "...";
String s1 = Normalizer.normalize(s, Normalizer.Form.NFKD);
这使用Normalizer来分解字符串。这样做的好处是,可以排序,“c”和“ĉ”保持在一起。可以使用正则表达式删除组合变音符号,并且将具有ASCII版本。
事实上,不同的操作系统处理Unicode名称的方式不同,并不总是版本控制系统遵循跨平台规范化。
仅在与Normalizer.normalize
进行String.equals
比较后才确实表示Unicode文本相等。
答案 2 :(得分:0)
您的问题分为两部分:
规范形式意味着"标准化形式 - 在这种情况下是字段的小写版本,用于比较。每次值更改时,都必须更新小写副本,因此这个设计选择的开销很大。此外,这个想法只是针对性能的优化,并且坦率地不推荐,因为它过早优化"
equals的非对称性允许代码a.equals(b)
而不是b.equals(a)
,从而违反了equals合约。在您的示例中,String
可能等于您的类的实例,因为它的equals()
方法允许,但在字符串中实现equals()
class不允许将您的类的实例视为等于String
。