セットが等しいかどうかを単に知りたい場合、equals
onメソッド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);
}
以下の一般的なケースをどのように最適化するかに注意してください。
- 2つのオブジェクトは同じです
- 他のオブジェクトはまったくセットではありません。
- 2つのセットのサイズは異なります。
その後、このセットにはない他のセットの要素を見つけるとすぐにcontainsAll(...)
戻りfalse
ます。しかし、すべての要素が両方のセットに存在する場合、それらすべてをテストする必要があります。
したがって、最悪の場合のパフォーマンスは、2つのセットが等しいが同じオブジェクトではない場合に発生します。そのコストは通常、O(N)
またはO(NlogN)
の実装によって異なりthis.containsAll(c)
ます。
また、セットが大きく、要素のごく一部のみが異なる場合は、最悪のケースに近いパフォーマンスが得られます。
更新
カスタムセットの実装に時間を費やすことをいとわないのであれば、「ほぼ同じ」ケースを改善できるアプローチがあります。
アイデアは、セット全体のハッシュを事前に計算してキャッシュし、セットの現在のハッシュコード値をで取得できるようにする必要があるというものですO(1)
。次に、2つのセットのハッシュコードを加速として比較できます。
そのようなハッシュコードをどのように実装できますか?設定されたハッシュコードが次の場合:
- 空のセットの場合はゼロ、および
- 空でないセットのすべての要素ハッシュコードのXOR、
その後、要素を追加または削除するたびに、セットのキャッシュされたハッシュコードを安価に更新できます。どちらの場合も、要素のハッシュコードと現在設定されているハッシュコードを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 -N時間に1回だけ間違った答えを与えます。Nを十分に大きく(たとえば512ビット)すると、間違った答えの確率は無視できます(たとえば、およそ10 -150)。
欠点は、要素の暗号化チェックサムを計算することは、特にビット数が増えるにつれて非常にコストがかかることです。したがって、チェックサムをメモするための効果的なメカニズムが本当に必要です。そして、それは問題になる可能性があります。
もう1つの欠点は、確率がどれほど小さくても、ゼロ以外のエラーの確率は許容できない可能性があることです。(しかし、そうである場合...宇宙線が重要なビットをフリップするケースにどう対処しますか?それとも、冗長システムの2つのインスタンスで同じビットを同時にフリップする場合?)