JavaのJDKに並行リストはありますか?


277

インデックスによって要素にアクセスできるコンカレントリストインスタンスを作成するにはどうすればよいですか?JDKには、使用できるクラスまたはファクトリメソッドがありますか?


28
なぜ建設的ではないのですか?.Netにないいくつかの提案されたCopyOnWriteArrayList。両方の質問は互いに関連していると言えますが、これは閉じていません!!!
AlikElzin-kilaka 2011

1
Jarrod Robersonが、Stephanが行った詳細な編集を取り入れて、元の不適切な表現の質問に戻すのが良い考えだとなぜ思うのか、私にはわかりません。ジャロッドの答えはまだ完全に許容できるものです。実際、CopyOnWriteArrayListは、JDKでListを実装する唯一の並行クラスです。困惑しています...
Matt Passell、2015

10
受け入れ答えはにあったので、元の質問とステファンは、そのソースコードの束と全く関係のない質問を入れて、オリジナルのポスターが含まれていなかったのどこよりも、他のことを示唆したより多くの回答を生成し、完全に質問を変え、List特にその元を破壊行為と見なされる要件です。回答がその破壊されたバージョンの質問に回答しないと不平を言っている人々のために、モデレーターはすでに質問をロックしました。

1
/ locked/ closed/以前のコメント

8
この質問が閉じられる理由はありません。JDKのクラスについて質問します。これは、ライブラリを検索するようなものです。Javaのベースです。
maaartinus

回答:


175

java.util.concurrentには並行リストの実装があります。特にCopyOnWriteArrayList


84
すべての挿入でリスト全体をコピーするため、非効率になることがよくあります。
dfrankow 2013年

22
@dfrankowしかし、更新するよりもはるかに多く反復する場合は、より効率的になります。
b1nary.atr0phy 2015

ここに示すようにうまく機能しません。addAllメソッドを使用し、streamを使用してそれを読み取るだけですが、例外があります。stackoverflow.com/questions/1527519/...
devssh

166

インデックスベースのアクセスを気にせず、リストの挿入順序を維持する特性だけが必要な場合は、java.util.concurrent.ConcurrentLinkedQueueを検討できます。Iterableを実装しているため、すべての項目の追加が完了したら、拡張されたfor構文を使用してコンテンツをループできます。

Queue<String> globalQueue = new ConcurrentLinkedQueue<String>();

//Multiple threads can safely call globalQueue.add()...

for (String href : globalQueue) {
    //do something with href
}

4
私は(ステートメントのために簡略化することを考えて:のforeachと呼ばれている):docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
AlikElzin-kilaka

2
@ AlikElzin-kilakaそうですね。実際の構文には「each」という単語が含まれていないため、名前は常に私を悩ませてきたと思いますが、公式の名前を使用するように回答を更新します。:)
Matt Passell、2014

3
@ AlikElzin-kilaka Nitpickingですが、JLSバージョン8によれば、「拡張ステートメント」と呼ばれています。Javaチュートリアルでも同じです。
ローランド

1
@Rolandは間違いなくつまらない。Javaでは、「for each」と「enhanced for」の間に(現在)違いがあります。
hfontanez

2
@Rolandは間接的に。Stream.forEachと拡張版として知られているものとの混乱を排除するために、彼らは「for eachループ」の名前を「拡張版」に変更したと思います。
hfontanez 2018

128

単純な呼び出し同期が必要な場合は、Collections.synchronizedList(List)を使用できます。

 List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());

70
の結果synchronizedListは「同期」されますが、「同時」ではありません。多くのリスト操作(インデックスベース)は、それ自体がアトミックではなく、より大きな相互排除構造の一部である必要があるという1つの基本的な問題です。

6
IMO、aを指定することVectorは、よりも簡単ですCollections.synchronizedList(new ArrayList<Object>())
ステファン

8
synchronizedListの結果は「synchronized」ですが、「concurrent」ではありません。
Kanagavelu Sugumar 2014年

46

位置を取得し、指定された位置から要素を取得する動作には、当然、いくつかのロックが必要です(これらの2つの操作の間でリストに構造的な変更を加えることはできません)。

並行コレクションのまさにその考えは、それ自体の各操作はアトミックであり、明示的なロック/同期なしで実行できるということです。

したがって、アトミック操作としてn特定の要素から位置に要素を取得することはList、同時アクセスが予想される状況ではあまり意味がありません。


6
ヨアヒム、頭に釘を打ったと思います。たとえば、読み取り専用リストを並行リストとして考えます。リストから位置Nの要素を取得することは意味があるだけでなく、問題の要点でもあります。したがって、不変のリスト(小文字のL)は良い例ですが、リスト(大文字のL)ではありません。CopyOnWriteArrayListは同時に実行されますが、多くの人はパフォーマンスを気に入らないでしょう。ロープ(ストリングロープ)に沿った解決策は、おそらく良い勝者でしょう。
johnstosh 2013年

1
非常に良い点です。しかし、OPが使用するリストは、非常に具体的な使用法があるかもしれません。たとえば、並行環境で満たされ、「ロック」され(意味が何であれ)、インデックスによって安全にアクセスされる場合があります。そのため、このようなリストを埋める最初のフェーズでは、スレッドセーフな実装が必要になります。残念ながら、OPは彼が探しているリストがどのように使用されるかについては明確ではありませんでした。
igor.zh 2018

13

次のオプションがあります。

  • Collections.synchronizedList():あなたはどのラップすることができList、実装(ArrayListLinkedListまたはサードパーティのリスト)。すべてのメソッド(読み取りと書き込み)へのアクセスは、を使用して保護されsynchronizedます。iterator()forループを使用または拡張する場合は、手動で同期する必要があります。反復中、他のスレッドは読み取りからも完全にブロックされます。hasNextとのnext呼び出しごとに個別に同期することもできますが、その場合ConcurrentModificationExceptionは可能です。

  • CopyOnWriteArrayList:変更するにはコストがかかりますが、読むのに時間がかかります。イテレータがスローすることはなくConcurrentModificationException、イテレータが作成中に別のスレッドによってリストが変更された場合でも、イテレータの作成時にリストのスナップショットを返します。頻繁に更新されないリストに役立ちます。のような一括操作addAllは更新に適しています-内部配列のコピー回数は少なくなります。

  • Vector:と非常によく似てsynchronizedListいますが、反復も同期されます。ただし、反復中にConcurrentModificationExceptionベクターが別のスレッドによって変更された場合、反復子はをスローできます。

別のオプション:

  • Collections.unmodifiableList():ロックフリー、スレッドセーフ、ただし変更不可
  • QueueまたはDeque、リストの最後にのみ追加/削除してリストを反復処理する場合は、代替手段になる場合があります。インデックスによるアクセスや、任意の場所での追加/削除はありません。パフォーマンスと同時アクセス性が向上した複数の同時実装がありますが、この質問の範囲を超えています。JCToolsを確認することもできます。JCToolsには、単一のコンシューマーまたは単一のプロデューサーに特化した、よりパフォーマンスの高いキュー実装が含まれています。

4

ここに画像の説明を入力してください

CopyOnWriteArrayListは、基になる配列の新しいコピーを作成することによってすべての変更操作(追加、設定など)が実装されるArrayListのスレッドセーフなバリアントです。

CopyOnWriteArrayListは、同期されたListがListインターフェイスを実装し、java.util.concurrentパッケージとそのスレッドセーフコレクションの一部である並行処理の代替手段です。

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

CopyOnWriteArrayListはフェイルセーフであり、反復中に基になるCopyOnWriteArrayListが変更されたときにConcurrentModificationExceptionをスローしません。別のArrayListのコピーを使用します。

コピーアレイにはすべての更新操作が含まれるため、通常はコストがかかりすぎます。クローンコピーが作成されます。CopyOnWriteArrayListは、読み取り操作が頻繁に行われる場合にのみ最適です。

/**
         * Returns a shallow copy of this list.  (The elements themselves
         * are not copied.)
         *
         * @return a clone of this list
         */
        public Object clone() {
            try {
                @SuppressWarnings("unchecked")
                CopyOnWriteArrayList<E> clone =
                    (CopyOnWriteArrayList<E>) super.clone();
                clone.resetLock();
                return clone;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError();
            }
        }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.