私はここの人々が使用して何を考え、知って興味
org.apache.commons.lang.builder
EqualsBuilder
/をHashCodeBuilder
実装するためのequals
/をhashCode
?自分で書くよりも良い習慣でしょうか?Hibernateでうまく機能しますか?あなたの意見は何ですか?
私はここの人々が使用して何を考え、知って興味
org.apache.commons.lang.builder
EqualsBuilder
/をHashCodeBuilder
実装するためのequals
/をhashCode
?自分で書くよりも良い習慣でしょうか?Hibernateでうまく機能しますか?あなたの意見は何ですか?
回答:
commons / langビルダーは素晴らしく、目立ったパフォーマンスのオーバーヘッドなし(休止状態のあるなしにかかわらず)で何年も使用しています。しかし、アランが書いているように、グアバの方法はさらに優れています。
以下はサンプルBeanです。
public class Bean{
private String name;
private int length;
private List<Bean> children;
}
Commons / Langで実装されたequals()およびhashCode()は次のとおりです。
@Override
public int hashCode(){
return new HashCodeBuilder()
.append(name)
.append(length)
.append(children)
.toHashCode();
}
@Override
public boolean equals(final Object obj){
if(obj instanceof Bean){
final Bean other = (Bean) obj;
return new EqualsBuilder()
.append(name, other.name)
.append(length, other.length)
.append(children, other.children)
.isEquals();
} else{
return false;
}
}
そしてここでJava 7以上(Guavaに触発された):
@Override
public int hashCode(){
return Objects.hash(name, length, children);
}
@Override
public boolean equals(final Object obj){
if(obj instanceof Bean){
final Bean other = (Bean) obj;
return Objects.equals(name, other.name)
&& length == other.length // special handling for primitives
&& Objects.equals(children, other.children);
} else{
return false;
}
}
注:このコードは当初Guavaを参照していましたが、コメントが指摘したように、この機能はJDKで導入されたため、Guavaは不要になりました。
ご覧のとおり、Guava / JDKのバージョンは短く、余分なヘルパーオブジェクトを回避しています。等しい場合、以前のObject.equals()
呼び出しがfalseを返した場合に評価を短絡することもできます(公平を期すために:commons / langには、上記のように短絡を許可するObjectUtils.equals(obj1, obj2)
代わりに使用できる同じセマンティクスのメソッドがありますEqualsBuilder
)。
だから:はい、コモンズの言語ビルダーは手動で構築equals()
したhashCode()
メソッドよりも非常に好ましいです(またはEclipseが生成する恐ろしいモンスター)、しかしJava 7+ / Guavaバージョンはさらに優れています。
Hibernateについてのメモ:
equals()、hashCode()およびtoString()の実装で遅延コレクションを使用する場合は注意してください。開いているセッションがない場合、それは無惨に失敗します。
注(equals()について):
a)上記のequals()の両方のバージョンで、これらのショートカットの一方または両方を使用することもできます。
@Override
public boolean equals(final Object obj){
if(obj == this) return true; // test for reference equality
if(obj == null) return false; // test for null
// continue as above
b)equals()コントラクトの解釈に応じて、行を変更することもできます
if(obj instanceof Bean){
に
// make sure you run a null check before this
if(obj.getClass() == getClass()){
2番目のバージョンを使用する場合は、おそらくメソッドsuper(equals())
内でも呼び出す必要がありますequals()
。意見はここで異なります、トピックはこの質問で議論されます:
(についてですhashCode()
が、同じことがに適用されますequals()
)
注(kayahrからのコメントに触発されて)
Objects.hashCode(..)
(基礎となると同じようにArrays.hashCode(...)
)プリミティブフィールドが多い場合、パフォーマンスが低下する可能性があります。そのような場合、EqualsBuilder
実際にはより良い解決策かもしれません。
equals
。Guavaはすべての値をオブジェクトに変換します。commons-langは新しいオブジェクトを1つだけ作成します。
サードパーティのライブラリに依存したくない場合(リソースが限られたデバイスを実行している可能性があります)、独自のメソッドを入力したくない場合でも、たとえばeclipseを使用するなど、IDEにジョブを実行させることができます。
Source -> Generate hashCode() and equals()...
必要に応じて構成でき、変更時にサポートする必要がある「ネイティブ」コードを取得します。
例(Eclipse Juno):
import java.util.Arrays;
import java.util.List;
public class FooBar {
public String string;
public List<String> stringList;
public String[] stringArray;
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((string == null) ? 0 : string.hashCode());
result = prime * result + Arrays.hashCode(stringArray);
result = prime * result
+ ((stringList == null) ? 0 : stringList.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
FooBar other = (FooBar) obj;
if (string == null) {
if (other.string != null)
return false;
} else if (!string.equals(other.string))
return false;
if (!Arrays.equals(stringArray, other.stringArray))
return false;
if (stringList == null) {
if (other.stringList != null)
return false;
} else if (!stringList.equals(other.stringList))
return false;
return true;
}
}
equals
。サードパーティのライブラリに依存したくない場合は、Objects.equal
自分と同じように1行のメソッドを記述します。1回または2回だけ使用した場合でも、コードが大幅に改善されます。
equals
/ hashCode
一行メソッド???
EqualsBuilderとHashCodeBuilderには、手動で作成したコードとは異なる2つの主要な側面があります。
EqualsBuilderとHashCodeBuilderを使用すると、nullの可能性があるフィールドを簡単に比較できます。手動で記述されたコードでは、これは多くの定型文を作成します。
一方、EqualsBuilderは、equalsメソッド呼び出しごとにインスタンスを作成します。equalsメソッドが頻繁に呼び出される場合、これは多くのインスタンスを作成します。
Hibernateの場合、equalsとhashCodeの実装には違いがありません。これらは単なる実装の詳細です。hibernateでロードされたほとんどすべてのドメインオブジェクトでは、(エスケープ分析がなくても)Builderのランタイムオーバーヘッドを無視できます。データベースと通信のオーバーヘッドは大きくなります。
skaffmanが述べたように、リフレクションバージョンは製品コードでは使用できません。リフレクションは遅くなり、「実装」は最も単純なクラスを除くすべてに対して正しくありません。新しく導入されたメンバーは、equalsメソッドの動作を変更するため、すべてのメンバーを考慮することも危険です。リフレクションバージョンは、テストコードで役立ちます。
独自に作成しない場合は、Googleグアバ(以前のGoogleコレクション)を使用することもできます。
idが主キーであるエンティティBeanを処理しているだけであれば、単純化できます。
@Override
public boolean equals(Object other)
{
if (this == other) { return true; }
if ((other == null) || (other.getClass() != this.getClass())) { return false; }
EntityBean castOther = (EntityBean) other;
return new EqualsBuilder().append(this.getId(), castOther.getId()).isEquals();
}
私の意見では、それはHibernateでうまく機能しません。特に、エンティティーの長さ、名前、および子を比較する回答の例はそうではありません。Hibernateは、equals()およびhashCode()で使用されるビジネスキーを使用することをお勧めしますが、それらには理由があります。ビジネスキーでauto equals()およびhashCode()ジェネレーターを使用する場合は問題ありません。前述のように、パフォーマンスの問題だけを考慮する必要があります。しかし、人々は通常、IMOが非常に間違っているすべてのプロパティを使用します。たとえば、私は現在@AutoPropertyでPojomaticを使用してエンティティが記述されているプロジェクトに取り組んでいますが、これは本当に悪いパターンだと思います。
hashCode()とequals()を使用する2つの主なシナリオは次のとおりです。
したがって、エンティティが次のようになっていると仮定します。
class Entity {
protected Long id;
protected String someProp;
public Entity(Long id, String someProp);
}
Entity entity1 = new Entity(1, "a");
Entity entity2 = new Entity(1, "b");
どちらもHibernateの同じエンティティであり、ある時点であるセッションからフェッチされています(それらのIDとクラス/テーブルは同じです)。しかし、すべての小道具にauto equals()とhashCode()を実装すると、何が得られますか?
したがって、私が作成する99%のプロジェクトでは、ベースエンティティクラスで一度記述された次のequals()およびhashCode()の実装を使用します。これは、Hibernateの概念と一致しています。
@Override
public boolean equals(Object obj) {
if (StringUtils.isEmpty(id))
return super.equals(obj);
return getClass().isInstance(obj) && id.equals(((IDomain) obj).getId());
}
@Override
public int hashCode() {
return StringUtils.isEmpty(id)
? super.hashCode()
: String.format("%s/%s", getClass().getSimpleName(), getId()).hashCode();
}
一時エンティティの場合、私はHibernateが永続化ステップで行うのと同じことを行います。インスタンスマッチを使用します。永続オブジェクトについて、一意のキーであるtable / idを比較します(複合キーは使用しません)。
万が一、他の人が便利だと思う場合は、上記の余分なオブジェクト作成オーバーヘッドを回避するハッシュコード計算用のこのヘルパークラスを考えました(実際、Objects.hash()メソッドのオーバーヘッドは、各レベルで新しい配列を作成するため、継承!)
使用例:
public int hashCode() {
return HashCode.hash(HashCode.hash(timestampMillis), name, dateOfBirth); // timestampMillis is long
}
public int hashCode() {
return HashCode.hash(super.hashCode(), occupation, children);
}
HashCodeヘルパー:
public class HashCode {
public static int hash(Object o1, Object o2) {
return add(Objects.hashCode(o1), o2);
}
public static int hash(Object o1, Object o2, Object o3) {
return hash(Objects.hashCode(o1), o2, o3);
}
...
public static int hash(Object o1, Object o2, ..., Object o10) {
return hash(Objects.hashCode(o1), o2, o3, ..., o10);
}
public static int hash(int initial, Object o1, Object o2) {
return add(add(initial, o1), o2);
}
...
public static int hash(int initial, Object o1, Object o2, ... Object o10) {
return add(... add(add(add(initial, o1), o2), o3) ..., o10);
}
public static int hash(long value) {
return (int) (value ^ (value >>> 32));
}
public static int hash(int initial, long value) {
return add(initial, hash(value));
}
private static int add(int accumulator, Object o) {
return 31 * accumulator + Objects.hashCode(o);
}
}
私は10がドメインモデルのプロパティの最大の合理的な数であることを理解しました。もしもっと多くがある場合は、文字列とプリミティブのヒープを維持する代わりに、リファクタリングとより多くのクラスの導入を考える必要があります。
欠点は次のとおりです。深くハッシュする必要のあるプリミティブや配列が主にある場合は役に立ちません。(通常、これは、制御不能なフラット(転送)オブジェクトを処理する必要がある場合です。)
reflectionEquals
とreflectionHashcode
関数に誘惑されないでください。パフォーマンスは絶対的なキラーです。