リストの要素を比較するコードを最適化しようとしています。
例えば。
public void compare(Set<Record> firstSet, Set<Record> secondSet){
for(Record firstRecord : firstSet){
for(Record secondRecord : secondSet){
// comparing logic
}
}
}
セット内のレコード数が多くなることを考慮してください。
ありがとう
シェカル
firstSet.equals(secondSet)
それは本当に比較ロジックで何をしたいかに依存します。つまり、一方のセットにある要素をもう一方のセットにない場合はどうなりますか?メソッドにはvoid
戻り型があるため、このメソッドで必要な作業を行うことを想定しています。
必要に応じて、よりきめ細かな制御:
if (!firstSet.containsAll(secondSet)) {
// do something if needs be
}
if (!secondSet.containsAll(firstSet)) {
// do something if needs be
}
一方のセットにあり、もう一方のセットにはない要素を取得する必要がある場合。
EDIT:set.removeAll(otherSet)
は、セットではなくブール値を返します。 removeAll()を使用するには、セットをコピーしてから使用する必要があります。
Set one = new HashSet<>(firstSet);
Set two = new HashSet<>(secondSet);
one.removeAll(secondSet);
two.removeAll(firstSet);
one
とtwo
の内容が両方とも空の場合、2つのセットが等しいことがわかります。そうでない場合は、セットを不均等にする要素があります。
あなたは、レコードの数が多いかもしれないと述べました。基礎となる実装がHashSet
である場合、各レコードのフェッチはO(1)
時間で行われるため、それよりもはるかに良くなることはできません。 TreeSet
はO(log n)
です。
セットが等しいかどうかだけを知りたい場合、equals
のAbstractSet
メソッドは、おおよそ次のように実装されます。
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection c = (Collection) o;
if (c.size() != size())
return false;
return containsAll(c);
}
以下の一般的なケースを最適化する方法に注意してください。
その後、containsAll(...)
は、このセットにも含まれていない他のセットの要素を見つけるとすぐにfalse
を返します。ただし、両方のセットにすべての要素が存在する場合は、それらすべてをテストする必要があります。
したがって、最悪の場合のパフォーマンスは、2つのセットが等しいが同じオブジェクトではない場合に発生します。そのコストは、O(N)
の実装に応じて、通常O(NlogN)
またはthis.containsAll(c)
です。
また、セットが大きく、要素のわずかな割合でのみ異なる場合、最悪のケースに近いパフォーマンスが得られます。
UPDATE
カスタムセットの実装に時間をかけたい場合は、「ほぼ同じ」ケースを改善できるアプローチがあります。
アイデアは、セットの現在のハッシュコード値をO(1)
で取得できるように、セット全体のハッシュを事前に計算してキャッシュする必要があるということです。次に、2つのセットのハッシュコードを加速として比較できます。
そのようなハッシュコードをどのように実装できますか?設定されたハッシュコードが次の場合:
その後、要素を追加または削除するたびに、セットのキャッシュされたハッシュコードを安価に更新できます。どちらの場合も、要素のハッシュコードと現在のセットのハッシュコードを単にXORします。
もちろん、これは、要素がセットのメンバーである間、要素のハッシュコードが安定していることを前提としています。また、要素クラスのハッシュコード関数が適切な広がりを与えると想定しています。これは、2つのセットのハッシュコードが同じ場合でも、すべての要素のO(N)
比較にフォールバックする必要があるためです。
少なくとも理論的には、この考えをもう少し進めることができます。
セット要素クラスに、要素の暗号チェックサムを返すメソッドがあると仮定します。次に、要素に対して返されたチェックサムをXORすることにより、セットのチェックサムを実装します。
これで何が買われますか?
さて、アンダーハンドが発生していないと仮定すると、2つの等しくないセット要素が同じNビットチェックサムを持つ確率は2です。-N。また、2つの等しくないセットが同じNビットチェックサムを持つ確率も2です。-N。したがって、私の考えは、equals
を次のように実装できるということです。
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection c = (Collection) o;
if (c.size() != size())
return false;
return checksums.equals(c.checksums);
}
上記の仮定の下では、これは2回に1回だけ間違った答えを与えます-N 時間。 Nを十分に大きくすると(例:512ビット)、間違った回答の確率は無視できるようになります(例:およそ10-150)。
マイナス面は、要素の暗号チェックサムの計算は、特にビット数が増えるにつれて非常に高価になることです。したがって、チェックサムをメモするための効果的なメカニズムが本当に必要です。そして、それは問題になる可能性があります。
グアバSets
には、ここで役立つメソッドがあります:
public static <E> boolean equals(Set<? extends E> set1, Set<? extends E> set2){
return Sets.symmetricDifference(set1,set2).isEmpty();
}
次のような非常に特殊なケースには、O(N)ソリューションがあります。
次のコードは、両方のセットが同等のレコードに基づいていることを前提としています。同様の方法は、コンパレータに基づいている可能性があります。
public class SortedSetComparitor <Foo extends Comparable<Foo>>
implements Comparator<SortedSet<Foo>> {
@Override
public int compare( SortedSet<Foo> arg0, SortedSet<Foo> arg1 ) {
Iterator<Foo> otherRecords = arg1.iterator();
for (Foo thisRecord : arg0) {
// Shorter sets sort first.
if (!otherRecords.hasNext()) return 1;
int comparison = thisRecord.compareTo(otherRecords.next());
if (comparison != 0) return comparison;
}
// Shorter sets sort first
if (otherRecords.hasNext()) return -1;
else return 0;
}
}
https://www.mkyong.com/Java/java-how-to-compare-two-sets/ から次のソリューションがあります
public static boolean equals(Set<?> set1, Set<?> set2){
if(set1 == null || set2 ==null){
return false;
}
if(set1.size() != set2.size()){
return false;
}
return set1.containsAll(set2);
}
または、単一のreturnステートメントを使用する場合:
public static boolean equals(Set<?> set1, Set<?> set2){
return set1 != null
&& set2 != null
&& set1.size() == set2.size()
&& set1.containsAll(set2);
}
Guava
ライブラリを使用している場合、次のことが可能です。
SetView<Record> added = Sets.difference(secondSet, firstSet);
SetView<Record> removed = Sets.difference(firstSet, secondSet);
そして、これらに基づいて結論を出します。
比較する前に、secondSetをHashMapに配置します。この方法で、2番目のリストの検索時間をn(1)に短縮します。このような:
HashMap<Integer,Record> hm = new HashMap<Integer,Record>(secondSet.size());
int i = 0;
for(Record secondRecord : secondSet){
hm.put(i,secondRecord);
i++;
}
for(Record firstRecord : firstSet){
for(int i=0; i<secondSet.size(); i++){
//use hm for comparison
}
}
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Set<String> a = this;
Set<String> b = o;
Set<String> thedifference_a_b = new HashSet<String>(a);
thedifference_a_b.removeAll(b);
if(thedifference_a_b.isEmpty() == false) return false;
Set<String> thedifference_b_a = new HashSet<String>(b);
thedifference_b_a.removeAll(a);
if(thedifference_b_a.isEmpty() == false) return false;
return true;
}