web-dev-qa-db-ja.com

STLのベクトルとリスト

私はEffective STLでそれに気づいた

vectorは、デフォルトで使用されるべきシーケンスのタイプです。

どういう意味ですか?効率を無視してもvectorは何でもできるようです。

vectorが実行可能なオプションではないがlistを使用しなければならないというシナリオを誰かに教えてもらえますか

205
skydoor

シーケンスの最後以外の場所に多数の項目を繰り返し挿入する状況。

それぞれの異なる種類のコンテナに対する複雑さの保証をチェックしてください。

標準コンテナの複雑さを保証するものは何ですか?

84
Martin York

vector:

  • 連続したメモリ.
  • 将来の要素にスペースを事前に割り当てます。そのため、要素自体に必要なスペースを超えて追加のスペースが必要になります。
  • 各要素は、要素型自体のためのスペースのみを必要とします(追加のポインタは必要ありません)。
  • 要素を追加したときはいつでもベクトル全体にメモリを再割り当てできます。
  • 最後の挿入は一定の償却時間ですが、それ以外の挿入はコストのかかるO(n)です。
  • ベクトルの終わりの消去は一定時間ですが、それ以外の場合はO(n)です。
  • あなたはランダムにその要素にアクセスすることができます。
  • ベクトルに要素を追加したりベクトルから要素を削除したりすると、イテレータは無効になります。
  • 要素の配列が必要な場合は、基礎となる配列に簡単にアクセスできます。

list:

  • 連続していないメモリ.
  • 事前に割り当てられたメモリはありません。リスト自体のメモリオーバーヘッドは一定です。
  • 各要素には、その要素を保持するノード用に、リスト内の次の要素と前の要素へのポインタを含む、追加のスペースが必要です。
  • 要素を追加したからといって、リスト全体にメモリを再割り当てする必要はありません。
  • 挿入と消去は、リスト内のどこに発生しても安価です。
  • リストとスプライシングを組み合わせるのは簡単です。
  • ランダムに要素にアクセスすることはできないので、リスト内の特定の要素にアクセスするのは面倒です。
  • リストに要素を追加または削除しても、反復子は有効なままです。
  • 要素の配列が必要な場合は、基になる配列がないため、新しい要素を作成してそれらすべてを追加する必要があります。

一般に、使用しているシーケンシャルコンテナの種類が気にならない場合はvectorを使用しますが、コンテナ内の最後以外の場所で多数の挿入または削除を行っている場合は、必要になります。リストを使用する。ランダムアクセスが必要な場合は、リストではなくベクトルが必要になります。それ以外に、あなたのアプリケーションに基づいてどちらかが必要になることは当然ありますが、一般的に、それらは良いガイドラインです。

371

要素を頻繁に挿入する必要がない場合は、ベクトルの方が効率的です。リストよりもはるかに優れたCPUキャッシュローカリティを持っています。つまり、ある要素にアクセスすると、veryとなり、次の要素がキャッシュに存在し、低速のRAMを読み取らなくても検索できるようになります。

30
Hans Passant

ここでのほとんどの回答は、1つの重要な詳細を見逃しています。

あなたは何をコンテナに入れたいですか?

それがintsのコレクションである場合、std::listは再割り当てが可能であるかどうかに関わらず、すべてのシナリオで失われます。先頭から削除するだけなどです。 list<int>vector<int>を上回る例を用意するのは非常に難しいでしょう。それでも、deque<int>はリストの使用を正当化するのではなく、より良いか近いかもしれません。

しかし、巨大で醜いデータの塊を扱うのであれば(そしてそれらのうちのいくつかは)、挿入時に過剰割り当てをしたくない場合、再割り当てによるコピーは厄介なことになるでしょう。 list<UglyBlob>よりvector<UglyBlob>

それでも、あなたがvector<UglyBlob*>あるいはvector<shared_ptr<UglyBlob> >にさえ切り替えるならば、再び - リストは遅れます。

そのため、アクセスパターン、ターゲット要素数などは依然として比較に影響しますが、私の考えでは - 要素のサイズ - コピーのコストなど.

26
Tomasz Gandor

Std :: listの特別な機能の1つは、スプライシング(リストの一部または全体を別のリストにリンクまたは移動すること)です。

または、あなたのコンテンツがコピーするのに非常に高価であるならば。そのような場合、例えば、リストを使ってコレクションをソートする方が安価な場合があります。

コレクションが小さい(そして内容がコピーするのに特に高価ではない)場合でも、どこかに挿入して消去したとしても、ベクトルはまだリストを上回る可能性があります。リストは各ノードを個別に割り当てるので、いくつかの単純なオブジェクトを移動するよりもはるかにコストがかかる可能性があります。

それほど難しいルールはないと思います。それはあなたがコンテナに対して何をしたいのか、そしてコンテナがどのくらいの大きさになるのか、そして含まれている型にもよります。ベクトルは、内容を単一の連続ブロックとして割り当てるため、一般的にリストを切り捨てます(基本的に動的に割り当てられる配列です。ほとんどの場合、配列は最も効率的な方法です)。

16
UncleBens

私のクラスの生徒たちは、ベクトルを使うほうが効果的であることを説明できないようですが、リストを使うように忠告しているときは、とても幸せそうです。

これが私の理解の仕方です

Lists:各項目には次または前の要素へのアドレスが含まれているため、この機能を使用すると、項目が並べ替えられていなくても順序を変えることができません。あなたの記憶は断片化しています。しかし、それには他にも非常に大きな利点があります。項目を簡単に挿入/削除できるのは、ポインターを変更することだけが必要だからです。欠点:ランダムな単一のアイテムを読むには、正しいアドレスが見つかるまであるアイテムから別のアイテムにジャンプする必要があります。

Vectors:ベクトルを使うとき、メモリは通常の配列のようにもっと組織化されています:各n番目の項目は(n-1)番目の項目の直後と(n + 1)番目の項目の直前に格納されます。なぜリストよりいいの?高速ランダムアクセスが可能だからです。ベクトル内のアイテムのサイズがわかっていて、それらがメモリ内で連続している場合は、n番目のアイテムがどこにあるかを簡単に予測できます。あなたが欲しいものを読むためにリストのすべての項目を閲覧する必要はありません、ベクトルで、あなたはあなたができないリストで、あなたは直接それを読みます。一方、ベクトル配列を変更したり、値を変更したりするのははるかに遅くなります。

リストは、メモリ内で追加/削除できるオブジェクトを追跡するのにより適しています。ベクトルは、大量の単一アイテムから要素にアクセスする場合に適しています。

リストの最適化方法はわかりませんが、高速読み取りアクセスが必要な場合はベクトルを使用する必要があることを知っておく必要があります。STLがリストを高速化するのは、ベクトルより読み取りアクセスが速いからではありません。

13
jokoon

基本的にベクトルは自動メモリ管理を備えた配列です。データはメモリ内で連続しています。途中でデータを挿入しようとすると、コストがかかります。

リストでは、データは関連性のないメモリ位置に保存されます。真ん中に挿入することは、新しいもののために場所を空けるためにデータの一部をコピーすることを含みません。

より具体的にあなたの質問に答えるために私は引用します このページ

ベクトルは一般に、要素にアクセスし、シーケンスの最後に要素を追加または削除するのに時間的に最も効率的です。末尾以外の位置で要素を挿入または削除する操作の場合、それらはdequesおよびlistよりもパフォーマンスが悪く、listよりも一貫性のない反復子および参照を持ちます。

10
f4.

イテレータを無効にすることはできません。

9
dirkgently

あなたがシーケンスの途中で多くの挿入または削除があるとき。例えばメモリマネージャ.

8
AraK

リストを使用する理由の1つは、反復子の有効性を維持することです。もう1つは、アイテムをプッシュするときにベクトルを再割り当てしたくない場合です。これは、reserve()を賢く使用することで管理できますが、場合によっては、単にリストを使用するほうが簡単か、または実行可能な場合があります。

4
John Dibling

オブジェクトをコンテナ間で移動したい場合は、list::spliceを使用できます。

例えば、グラフ分割アルゴリズムは、増加する数のコンテナ間で再帰的に分割される一定数のオブジェクトを有することができる。オブジェクトは一度初期化する必要があり、常にメモリ内の同じ場所に残ります。再割り当てするよりも再リンクすることでそれらを再配置する方がはるかに速いです。

編集:ライブラリがC++ 0xを実装する準備をしているので、サブシーケンスをリストにつなぎ合わせる一般的なケースは、シーケンスの長さとともに線形の複雑さになりつつあります。これは、splice(now)がシーケンス内の要素数を数えるためにシーケンスを反復処理する必要があるためです。単純にリストを数えて再リンクするのは、他の方法よりもずっと速く、リスト全体または単一の要素を接合することは、一定の複雑さを伴う特別な場合です。しかし、つなぎ合わせのシーケンスが長い場合は、古くなった、より準拠していない、より優れたコンテナを探してみる必要があるかもしれません。

4
Potatoswatter

listを使用する必要がある唯一の厳格な規則は、ポインタをコンテナの要素に配布する必要がある場合です。

vectorとは異なり、要素のメモリは再割り当てされません。もしそうなら、あなたは未使用のメモリへのポインタを持っているかもしれません。それは、せいぜい大きく、いいえ、そして、悪くてもSEGFAULTです。

(技術的には*_ptrvectorも機能しますが、その場合はlistをエミュレートしているので、これは単なるセマンティクスです。)

他のソフトルールはコンテナの真ん中に要素を挿入することで起こりうるパフォーマンスの問題と関係があります。そこでlistが望ましいでしょう。

1
iPherian