比較したい同じタイプのオブジェクトのコレクションが2つあります。この場合、オブジェクトのequals()
を考慮しない属性に基づいてそれらを比較したいと思います。私の例では、たとえば、ランク付けされた名前のコレクションを使用しています。
_public class Name {
private String name;
private int weightedRank;
//getters & setters
@Override
public boolean equals(Object obj) {
return this.name.equals(obj.name); //Naive implementation just to show
//equals is based on the name field.
}
}
_
2つのコレクションを比較して、各コレクションの位置i
について、その位置の各名前のweightedRank
が同じ値であることを表明したいと思います。グーグルを実行しましたが、Commons Collectionsやその他のAPIで適切なメソッドが見つからなかったため、次のことを思いつきました。
_public <T> boolean comparatorEquals(Collection<T> col1, Collection<T> col2,
Comparator<T> c)
{
if (col1 == null)
return col2 == null;
if (col2 == null)
return false;
if (col1.size() != col2.size())
return false;
Iterator<T> i1 = col1.iterator(), i2 = col2.iterator();
while(i1.hasNext() && i2.hasNext()) {
if (c.compare(i1.next(), i2.next()) != 0) {
return false;
}
}
return true;
}
_
これを行う別の方法はありますか? CommonsCollectionsの明らかな方法を見逃しましたか?
私も見つけました この質問 on SOこれは似ていますが、その場合はequals()
をオーバーライドする方が少し理にかなっていると思います。
これに非常に似たものが、近い将来(この記事の執筆時点で) Apache Commons Collections のリリースに入る予定です。 https://issues.Apache.org/jira/browse/COLLECTIONS-446 を参照してください。
この方法が実際に優れているかどうかはわかりませんが、「別の方法」です...
元の2つのコレクションを取得し、各ベースオブジェクトのアダプターを含む新しいコレクションを作成します。アダプターには、.equals()
および.hashCode()
がName.calculateWeightedRank()
に基づくものとして実装されている必要があります。次に、通常のコレクションの等式を使用して、アダプターのコレクションを比較できます。
*編集*
Adapter
にEclipseの標準hashCode/equals生成を使用します。コードは、各基本コレクションでadaptCollectionを呼び出し、次に2つの結果をList.equals()で呼び出します。
public class Adapter {
public List<Adapter> adaptCollection(List<Name> names) {
List<Adapter> adapters = new ArrayList<Adapter>(names.size());
for (Name name : names) {
adapters.add(new Adapter(name));
}
return adapters;
}
private final int name;
public Adapter(Name name) {
this.name = name.getWeightedResult();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + name;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Adapter other = (Adapter) obj;
if (name != other.name)
return false;
return true;
}
}
「比較」と「同等性」の概念を切り離すために、Guava Equivalent クラスを使用できます。 Comparatorの代わりにEquivalentサブクラスを受け入れる比較メソッド(AFAIK Guavaにはありません)を作成する必要がありますが、少なくともコードの混乱は少なく、同等の基準に基づいてコレクションを比較できます。
等価ラップされたオブジェクトのコレクションを使用すると( 等価のwrapメソッド を参照)、sharakanによって提案されたアダプターベースのソリューションに似ていますが、等価実装はアダプター実装から分離され、次のことが可能になります。複数の等価基準を簡単に使用できます。
バージョン4以降にisEqualCollection
に追加された新しいCollectionUtils
メソッドを使用できます。このメソッドは、Equator
インターフェース実装によって提供される外部比較メカニズムを使用します。次のjavadocsを確認してください: CollectionUtils.isEqualCollection(...) および Equator 。
[〜#〜] edit [〜#〜]:古い回答を削除しました。
もう1つのオプションは、次のようなWeighted
というインターフェイスを作成することです。
public interface Weighted {
int getWeightedRank();
}
次に、Name
クラスにこのインターフェイスを実装してもらいます。次に、メソッドを次のように変更できます。
public <T extends Weighted> boolean weightedEquals(Collection<T> col1, Collection<T> col2)
{
if (col1 == null)
return col2 == null;
if (col2 == null)
return false;
if (col1.size() != col2.size())
return false;
Iterator<T> i1 = col1.iterator(), i2 = col2.iterator();
while(i1.hasNext() && i2.hasNext()) {
if (i1.next().getWeightedRank() != i2.next().getWeightedRank()) {
return false;
}
}
return true;
}
次に、重み付けして比較する必要のある追加のクラスを見つけたら、それらをコレクションに入れて、相互に比較することもできます。ただのアイデア。