今日読んだところ
45 ページ〜 66 ページ
hashCode 2 契約
- シグニフィカントなフィールドを変更しなければ、いつ hashCode を呼び出しても値は変わらない
- x.equals(y) が true の場合は、 x.hashCode() == y.hashCode() が true
equals をオーバーライドしたら hashCode もオーバーライドすべき
前述の 2 契約を守るために必要
x.equals(y) が false の場合
かならずしも、 x.hashCode() != y.hashCode() になる必要はないが、
値がバラけているほうが HashMap のキーとして使ったときのパフォーマンスがいい。
パフォーマンスについて
hashCode 関数のコストをケチって HashMap の性能が悪くなったら本末転倒
hashCode の作り方
適当なゼロでない定数に、シグニフィカントなフィールのハッシュ値を
result = 31 * result + field.hashCode()
と言う感じで合体させていけばいい
プリミティブな値の hashCode
- boolean
- hashCode = x ? 0 : 1;
- byte, char, short, int
- hashCode = (int) x;
- long
- hashCode = (int) (x ^ (x >>> 32));
- float
- hashCode = Float.floatToIntBits(x);
- double
- long y = Double.doubleToLongBits(x); hashCode = (int) (y ^ (y >>> 32));
他の hashCode
- null
- hashCode = 0;
- List
- Arrays.hashCode(x);
でも結局 hashCode に関しては
ここまで読んであれだけど Apache Commons の HashCodeBuilder 使えし!
http://commons.apache.org/lang/api/org/apache/commons/lang/builder/HashCodeBuilder.html
まさに外道!
あ、そういえば equals も
EqualsBuilder 使えし!
http://commons.apache.org/lang/api/org/apache/commons/lang/builder/EqualsBuilder.html
な、なんだってー!!
もちろん toString ビルダーもね!
ToStringBuilder 使えし!
http://commons.apache.org/lang/api/org/apache/commons/lang/builder/ToStringBuilder.html
まいりましたーーー!!!
たとえば、以下のようなデータクラス
public class AdministrativeDivision { private String unit; private String name; public AdministrativeDivision(String name, String unit) { this.unit = unit; this.name = name; } public void setUnit(String unit) { this.unit = unit; } public String getUnit() { return unit; } public void setName(String name) { this.name = name; } public String getName() { return name; } }
ちょこちょこっといじって…
public class AdministrativeDivision { // 略 @Override public String toString() { return ToStringBuilder.reflectionToString(this); } @Override public boolean equals(Object o) { return EqualsBuilder.reflectionEquals(this, o); } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } }
はい!完成っと!
おおおおおおおおすげーリフレクション楽だー!
シグニフィカントなフィールドが特定されている場合は
以下のような感じ
public class AdministrativeDivision { // 略 @Override public String toString() { return new ToStringBuilder(this).append("name", name).append("unit", unit).toString(); } @Override public boolean equals(Object o) { if (o == null) { return false; } if (o == this) { return true; } if (o.getClass() != getClass()) { return false; } AdministrativeDivision rhs = (AdministrativeDivision) o; return new EqualsBuilder() .appendSuper(super.equals(o)) .append(name, rhs.name) .append(unit, rhs.unit) .isEquals(); } @Override public int hashCode() { return new HashCodeBuilder().append(name).append(unit).toHashCode(); } }
すげーよ! Apache Commons 便利すぎるよ!
clone
clone の中で new はしない。
super.clone で生成されたオブジェクトをイジって返す。
持っているリファレンスは clone して super.clone の結果にくっつけてやる。
とかとか。
てか、 clone って使わないよなー。という話に。
static ファクトリメソッドはやしてやったほうがいいんじゃないかという話になった。
compareTo 4 契約
- signum(x.compareTo(y)) == -signum(y.compareTo(x))
- x.compareTo(y) が例外をスローする場合は、 y.compareTo(x) も例外をスローしなければならない
- x.compareTo(y) が例外をスローしない場合は、 y.compareTo(x) も例外をスローしてはいけない
- (x.compareTo(y) > 0 && y.compareTo(z) > 0) が true なら x.comparetTo(z) > 0 も true (推移的)
- x.compareTo(y) == 0 が true なら signum(x.comparetTo(z)) == signum(y.compareTo(z)) は必ず true
- x.compareTo(y) == 0 が true なら x.equals(y) (これは、強く推奨されているが絶対ではない)
double, float の compareTo と equals
double, float は NaN や -0.0 のとき == や < や > などの演算子では、 equals 契約や compareTo 契約を守れないため
Double.equals, Float.equals, Double.compare, Float.compare を使わなければならない
引き算による実装
オーバーフローするのでダメよ。
っていうか
ざゎ…ざゎ…
@hikoma っくす 「CompareToBuilder 使えば」
http://commons.apache.org/lang/api/org/apache/commons/lang/builder/CompareToBuilder.html
きたー!
と言うわけで
今日は、 Apache Commons にじゃっばじゃばにされちゃいました><