equals
カスタムコンパレータを実装する場合、それ以外にオーバーライドすることは良い習慣と見なされcompare
ますか?
さらに、?の定義済みの契約はありComparator
ますか?
5 に答える
コンパレータのjavadocには次のように記載されています。
コンパレータcによって要素Sのセットに課される順序は、c.compare(e1、e2)== 0がすべてのe1に対してe1.equals(e2)と同じブール値を持っている場合に限り、equalsと一致すると言われます。およびSのe2。
ソートされたセット(またはソートされたマップ)を順序付けるために、equalsと矛盾する順序を課すことができるコンパレータを使用する場合は注意が必要です。明示的なコンパレータcを持つソートされたセット(またはソートされたマップ)が、セットSから引き出された要素(またはキー)で使用されると仮定します。 「奇妙に」振る舞う。特に、ソートされたセット(またはソートされたマップ)は、等しいという観点から定義されたセット(またはマップ)の一般的な契約に違反します。
したがって、答えは「はい」です。これは良い習慣です(少なくとも最初の質問については)。
Comparatorのコントラクトは、そのjavadocで定義されています。特に:
ソートされたセット(またはソートされたマップ)を順序付けるために、equalsと矛盾する順序を課すことができるコンパレータを使用する場合は注意が必要です。明示的なコンパレータcを持つソートされたセット(またはソートされたマップ)が、セットSから引き出された要素(またはキー)で使用されると仮定します。 「奇妙に」振る舞う。特に、ソートされたセット(またはソートされたマップ)は、等しいという観点から定義されたセット(またはマップ)の一般的な契約に違反します。
通常、2つのオブジェクトがパースペクティブからは等しいが、equals
パースペクティブからは等しくない場合compareTo
、両方のオブジェクトをキーとしてTreeMapに格納できます。これは、直感的でない動作につながる可能性があります。また、特定の状況で意図的に行うこともできます。
たとえば、この回答は、 equalsとcompareToが矛盾していることが望ましい例を示しています。
平等をテストしていると感じた場合にのみ、等号をオーバーライドする必要があると思います。多くの場合、コンパレータを作成すると、オブジェクト内のプロパティが比較され、これらのプロパティでcompareToを引き続き使用します。
例えば
public class ToCompare {
public static final Comparator<ToCompare> ID_COMPARATOR = new Comparator(){
@Override
public int compare(ToCompare o1, ToCompare o2) {
return o1.id.compareTo(o2.id);
}
}
private Integer id;
//... other fields here
}
私はこれらのコンパレータをオブジェクトに格納する傾向があります。これらのコンパレータへのパブリック静的最終フィールドは、このオブジェクトをソートする可能性のある任意のコードからアクセスできます。オブジェクトが更新された場合(フィールドが追加/削除された場合)、コード検索の問題をすべて調べるのではなく、コンパレータに問題があるかどうかをすぐに確認できます。
はい。Comparatorのjavadocで提案されて いるように、それはあなたのニーズに依存すると思います。
たとえば、コンパレータcを使用して空のTreeSetに(a.equals(b)&& c.compare(a、b)!= 0)となる2つの要素aとbを追加するとします。Set.addメソッドの仕様に反していても、aとbはツリーセットの観点からは同等ではないため、2番目のadd操作はtrueを返します(ツリーセットのサイズが大きくなります)。
ただし、特定のリストを並べ替えるだけでよい以下の例を検討すると、並べ替える必要がない場合があります。
Employee
2つのプロパティを持つid
クラスの例を見てみましょうname
。
class Employee {
int id;// sample so access not considered
String name;// sample so access not considered
}
リストを並べ替えるオプションが2つあるとしid
ますname
。これを実現するには、2つのコンパレータを実装する必要があります。1つは名前で比較しid
、もう1つcompares
は名前で比較します。
今、私がコンパレータを実装したい場合id
、そのタイプはint
ラッパータイプでInteger
あり、メソッドをInteger
提供するので、それを使用します。compareTo
の場合も同様ですString
。compare
このように、あなたは書くことやcompareTo
方法についてほとんど心配する必要はありません。
compare
私は常にオブジェクトのプロパティに組み込まれたメソッドを使用するので、メソッドを作成するときに考える必要はほとんどありません。
class EmpIdComparator implements Comparator<Employee> {
@Override
public int compare(Employee o1, Employee o2) {
return Integer.valueOf(o1.id).compareTo(o2.id);// Make use of
// existing
// comparators
// provided by java
}
}
class EmpNameComparator implements Comparator<Employee> {
@Override
public int compare(Employee o1, Employee o2) {
return o1.name == null ? o2.name == null ? 0 : 1 : o1.name
.compareTo(o2.name);// Make use of
// existing
// comparators
// provided by java
}
}
他の答えは要点を見逃しているように感じますComparator
。'scompare-methodは2つの指定されたパラメーターを比較し、Comparator
'sequals-methodはComparator
それ自体を別のオブジェクトと比較します。
Comparator#equals
少なくとも新しいJDKのjavadocは、それを詳細に説明しています。
/**
* Indicates whether some other object is "equal to" this
* comparator. This method must obey the general contract of
* {@link Object#equals(Object)}. Additionally, this method can return
* <tt>true</tt> <i>only</i> if the specified object is also a comparator
* and it imposes the same ordering as this comparator. Thus,
* <code>comp1.equals(comp2)</code> implies that <tt>sgn(comp1.compare(o1,
* o2))==sgn(comp2.compare(o1, o2))</tt> for every object reference
* <tt>o1</tt> and <tt>o2</tt>.<p>
*
* Note that it is <i>always</i> safe <i>not</i> to override
* <tt>Object.equals(Object)</tt>. However, overriding this method may,
* in some cases, improve performance by allowing programs to determine
* that two distinct comparators impose the same order.
*
* @param obj the reference object with which to compare.
* @return <code>true</code> only if the specified object is also
* a comparator and it imposes the same ordering as this
* comparator.
* @see Object#equals(Object)
* @see Object#hashCode()
*/
boolean equals(Object obj);
そのため、のequals-methodをオーバーライドすることはほとんど役に立ちませんComparator
。Comparable
しかし、違うでしょう。