web-dev-qa-db-ja.com

配列vsベクトルvsリスト

10エントリの固定長テーブルを維持しています。各アイテムは、4つのフィールドのような構造です。数値位置で指定された挿入、更新、削除操作があります。この情報テーブルを維持するために使用するのに最適なデータ構造はどれかと思います。

  1. 配列-シフトのために挿入/削除には線形時間がかかります。更新には一定の時間がかかります。ポインターにスペースは使用されません。 []を使用してアイテムにアクセスする方が高速です。

  2. stlベクトル-シフトにより挿入/削除に線形時間がかかります。更新には一定の時間がかかります。ポインターにスペースは使用されません。アイテムへのアクセスは、operator []およびリンクリストの呼び出しであるため、配列よりも低速です。

  3. stl list-挿入/削除を適用する前に特定の位置まで繰り返す必要があるため、挿入と削除には線形時間がかかります。ポインター用に追加のスペースが必要です。アイテムへのアクセスは、リンクリストの線形トラバーサルであるため、配列よりも低速です。

今のところ、私の選択は配列を使用することです。それは正当ですか?または私は何かを見逃しましたか?

どちらが速いですか:リストを走査し、ノードを挿入するか、配列内のアイテムをシフトして空の位置を生成し、その位置にアイテムを挿入しますか?

このパフォーマンスを測定する最良の方法は何ですか?操作の前後にタイムスタンプを表示することはできますか?

41
jasonline

STL vectorを使用listと同等の豊富なインターフェイスを提供し、配列に必要なメモリ管理の苦痛を取り除きます。

operator[]のパフォーマンスコストを公開するには、一生懸命努力する必要があります。通常はインライン化されます。

数字はありませんが、挿入と削除の場合でも(もちろん一定のサイズで)vector<int>list<int>よりも高速であると説明したパフォーマンス分析を読んだことを覚えています。問題の真実は、私たちが使用しているこれらのプロセッサは非常に高速であるということです-そしてあなたのベクトルがL2キャッシュに収まるなら、それは本当に本当に速く行きます。一方、リストは、L2を強制終了するヒープオブジェクトを管理する必要があります。

50
Frank Krueger

早期最適化はすべての悪の根源です。

あなたの投稿に基づいて、ここでデータ構造の選択をパフォーマンスベースのものにする理由はないと思います。パフォーマンステストが問題を示している場合にのみ、最も便利なものを選択して変更に戻ります。

25
Don Neufeld

リストとベクトルの基本的な違いを理解するために、しばらく時間をかける価値があります。この2つの間の最も大きな違いは、要素の格納方法と追跡方法です。

-リスト-

リストには、前の要素と次の要素のアドレスが格納されている要素が含まれています。これは、リストのサイズに関係なく、一定の速度O(1)でリスト内の任意の要素を挿入または削除できることを意味します。その理由は、リストに挿入する要素の2つのポインター(前と次)を変更する必要があるだけだからです。

ランダムアクセスが必要な場合、リストは適切ではありません。したがって、リスト内のn番目の要素にアクセスする場合-リストを1つずつ走査する必要があります-O(n) speed

-ベクトル-

ベクターには、配列のように要素が順番に含まれています。これはランダムアクセスに非常に便利です。ベクトルの「n番目」の要素へのアクセスは、単純なポインター計算(O(1)速度)です。ただし、ベクターへの要素の追加は異なります。ベクターの途中に要素を追加したい場合-その要素の後に来るすべての要素は、新しいエントリ用のスペースを確保するために再割り当てする必要があります。速度は、ベクトルのサイズと新しい要素の位置に依存します。最悪のシナリオは、ベクトルの位置2に要素を挿入することです。最良のシナリオは、新しい要素を追加することです。したがって、挿入は速度O(n)で機能します。ここで、「n」は移動する必要のある要素の数であり、必ずしもベクトルのサイズではありません。

メモリ要件などを含む他の違いもありますが、リストとベクターが実際にどのように機能するかというこれらの基本原則を理解することは、本当に時間をかける価値があります。

いつものように... "早すぎる最適化はすべての悪の根源です"ですので、まずもっと便利なものを検討し、望みどおりに動作するように最適化してください。あなたが言及する10のエントリについては、それは本当にあなたが何を使用するかは関係ありません-あなたがどんな方法を使用してもパフォーマンスの違いを見ることができなくなります。

12
djogon

配列よりもstd :: vectorを優先します。ベクトルの利点は次のとおりです。

  • サイズが大きくなると、空き領域からメモリを割り当てます。
  • それらは変装したポインタではありません。
  • ランタイムのサイズを増減できます。
  • at()を使用して範囲チェックを行うことができます。
  • ベクトルはそのサイズを知っているので、要素を数える必要はありません。

ベクトルを使用する最も説得力のある理由は、明示的なメモリ管理から解放され、メモリがリークしないことです。ベクトルは、要素を格納するために使用するメモリを追跡します。ベクターが要素のためにより多くのメモリを必要とするとき、より多くを割り当てます。ベクトルが範囲外になると、そのメモリが解放されます。したがって、ユーザーは、ベクトル要素のメモリの割り当てと割り当て解除を気にする必要はありません。

6
Vijay Mathew

「operator []の呼び出しであるため、アイテムへのアクセスが配列よりも遅い」など、想定してはならない仮定を行っています。その背後にあるロジックは理解できますが、プロファイルを作成するまで、あなたも私も知ることができません。

最適化を有効にすると、オーバーヘッドがまったくないことがわかります。コンパイラーは関数呼び出しをインライン化します。 isメモリパフォーマンスの違いがあります。配列は静的に割り当てられ、ベクトルは動的に割り当てられます。リストはノードごとに割り当てられ、注意しないとキャッシュを調整できます。

いくつかの解決策は、スタックからvectorを割り当て、listのプールアロケーターを使用して、ノードがキャッシュに収まるようにすることです。

したがって、サポートされていない主張を心配するのではなく、設計をできるだけきれいにすることを心配する必要があります。それで、どちらがより理にかなっていますか?配列、ベクトル、またはリスト?あなたが何をしようとしているのか分からないので、答えられません。

「デフォルト」コンテナはvectorになる傾向があります。配列も完全に受け入れられる場合があります。

3
GManNickG

最初にいくつかのメモ:

データ構造の選択に関する経験則:一般的に、すべての可能性を検討し、配列が最良の選択であると判断した場合、最初からやり直します。あなたは何か非常に間違ったことをしました。

STLリストは_operator[]_をサポートしていません。また、配列のインデックス付けよりも遅くなる理由があった場合、関数呼び出しのオーバーヘッドとは関係ありません。

言われていること、ベクトルはここで明確な勝者です。 _operator[]_の呼び出しは、ベクトルの内容がメモリ内で連続していることが保証されているため、本質的に無視できます。 insert()およびerase()操作をサポートしています。これらの操作は、配列を使用した場合に本質的に自分で記述する必要があります。基本的に、ベクターは本質的に、必要なすべての操作を既にサポートしているアップグレードされた配列であるという事実に要約されます。

1
Graphics Noob

10エントリの固定長テーブルを維持しています。各アイテムは、4つのフィールドのような構造です。数値位置で指定された挿入、更新、削除操作があります。この情報テーブルを維持するために使用するのに最適なデータ構造はどれかと思います。

この説明に基づいて、データ構造の途中で挿入と削除を行う場合はO(1)なので、listの方が適しているようです。このジレンマは、使用するのに最適な構造の最初の決定を行うために使用できる多数の質問につながります。誤った選択。

尋ねる必要がある質問は3つあります。 1つ目は、ランダム読み取りと比較して中央で削除/挿入を行う頻度です。 2番目は、イテレータと比較して数値の位置を使用することの重要性です。最後に、構造の順序が重要です。

最初の質問の答えがランダムである場合、ベクトル/配列がおそらくうまく機能するよりも、読み取りが一般的です。 operator []表記が使用されている場合でも、データ構造の反復はランダム読み取りとは見なされないことに注意してください。 2番目の質問では、数値の位置が絶対に必要な場合、ベクトル/配列が必要になりますが、これはパフォーマンスに影響を与える可能性があります。後のテストでは、数値の位置を使用した簡単なコーディングと比較して、このパフォーマンスヒットは無視できることが示される場合があります。最後に、順序が重要でない場合は、O(1)アルゴリズムを使用してベクトル/配列に挿入および削除できます。サンプルのアルゴリズムを以下に示します。

template <class T>
void erase(vector<T> & vect, int index) //note: vector cannot be const since you are changing vector
{
  vect[index]= vect.back();//move the item in the back to the index
  vect.pop_back(); //delete the item in the back
}

template <class T>
void insert(vector<T> & vect, int index, T value) //note: vector cannot be const since you are changing vector
{
  vect.Push_back(vect[index]);//insert the item at index to the back of the vector
  vect[index] = value; //replace the item at index with value
}
0
Zachary Kraus