2011-01-03 7 views
4

は同期Collectionを想像してみて:同期されたコレクションを複製する方法は?

Set s = Collections.synchronizedSet(new HashSet()) 

このコレクションのクローンを作成するための最良の方法は何ですか?

元のコレクションではクローンを同期させる必要はありませんが、複製されたコレクションを反復処理する場合は元のコレクションで同期を行う必要はありません。

+0

コピーの条件はなんですか?元のセットはコピー中に変更できますか?クローンによって何を達成しようとしていますか? (あなたの目標を達成するより効率的な方法を示唆するかもしれませんが、それがあなたに合っていることを確認するためにこれらの質問への答えが必要です) –

+0

@Berin Loritsch:はい、元のセットはコピー中に変更することができます。私はデータと同期の意味で元のコレクションから独立しているはずのセットからIteratorを取得しようとしています。 – MRalwasser

答えて

6

使用同期ブロック内のコピーコンストラクタ:あなたにも同期するコピーが必要な場合は

synchronized (s) { 
    Set newSet = new HashSet(s); //preferably use generics 
} 

、再度Collections.synchronizedSet(..)を使用しています。

Peterのコメントによると、元のセットの同期ブロックでこれを行う必要があります。 synchronizedSetのドキュメントは、このことについて、明示的である:

あなたは、露光回避次の作業を実行して、セットを同期避けることができ、その上

+3

残念ながら、これはボンネットの下でIteratorを使用するので、これを安全に行うためには同期をとる必要があります。 –

+0

@Peterはい、明らかに、イテレータを使わずにコレクションにアクセスする方法はほとんどありません –

+1

ストーリーの悪い面は、メソッドがどのように実装されているか知っていなければならないということです。 – Bozho

1

を反復するとき、ユーザーが手動で返されたセットで同期をとる必要があり元のセットのIteratorあなたが見ることができるようにCollections.SynchronizedCollection

public Object[] toArray() { 
    synchronized(mutex) {return c.toArray();} 
} 

から

Set newSet = new HashSet(Arrays.asList(s.toArray())); 

EDITは、ロック操作が行われ、全体の時間のために保持されています。このように、データの安全なコピーが取られます。イテレータが内部で使用されているかどうかは関係ありません。返される配列は、ローカルスレッドのみが参照するため、スレッドセーフな方法で使用できます。

注:これらの問題を回避するには、2004年にJava 5.0で追加された並行性ライブラリのSetを使用することをお勧めします。これにより、コレクションの型をより安全にすることができるため、

+0

まだイテレータを使用しています。 – Bozho

+0

真ですが、コピーを反復処理します。これはスレッドセーフです。 toArray()はアトミックなのでスレッドセーフです。 –

+0

私は 'toArray()'がアトミックであることを保証する場所は見当たりません - 少なくともこの場合は重要です。私が見る唯一の保証は、 'toArray()'を呼び出すと配列の新しいコピーが作成されるということです。私はその前提に自分のコードを賭けたくありません。配列をコピーするには時間がかかります。特に、ソースがノードによってサポートされている場合(すべてのセットとLinkedListsの場合)この場合、イテレータが必要になります。 –

3

同期セットを使用する場合、セット内のすべての要素にアクセスする同期オーバーヘッドが発生することを理解してください。 Collections.synchronizedSet()は、すべてのメソッドの同期を強制するシェルでセットをラップするだけです。おそらくあなたが本当に意図したものではないでしょう。 ConcurrentSkipListSetは、複数のスレッドがセットに書き込むマルチスレッド環境でパフォーマンスを向上させます。

ConcurrentSkipListSetは、あなたが次のことを実行できるようになります:

Set newSet = s.clone();//preferably use generics 

これは、スナップショット処理のためのセットのクローンを使用することも珍しくありません。それがあなたの後にある場合は、アイテムがすでに処理されているケースを処理するためのコードを少し追加することができます。複数のコピーセットに含まれるときどきのオブジェクトに関連するオーバーヘッドは、通常、Collections.concurrentSet()を使用した一貫したオーバーヘッドよりも少なくなります。

編集:私は、ConcurrentSkipListSetがCloneableであり、スレッドセーフclone()メソッドを提供していることに気付きました。 Collections.concurrentSet()にスケーラビリティとパフォーマンスを失うのではなく、これが最良の選択だと本当に信じているので、私の答えは変わった。

関連する問題