現在、一意の文字列識別子に整数値を格納するstd::map<std::string,int>
があり、文字列を検索します。それは、挿入順序を追跡しないことを除いて、ほとんど私が望むことをします。したがって、マップを反復処理して値を出力すると、文字列に従って並べ替えられます。しかし、(最初の)挿入の順序に従ってソートされるようにします。
代わりにvector<pair<string,int>>
を使用することを考えましたが、文字列を検索して整数値を約10,000,000回インクリメントする必要があるため、std::vector
が大幅に遅くなるかどうかわかりません。
std::map
を使用する方法はありますか、それとも私のニーズに合った別のstd
コンテナーがありますか?
[私はGCC 3.4を使用しています。おそらく、std::map
]には50ペア以下の値しかありません。
ありがとう。
Std :: mapに50個の値しかない場合、出力する前にそれらをstd :: vectorにコピーし、適切なファンクターを使用してstd :: sortを介してソートできます。
または、 boost :: multi_index を使用できます。複数のインデックスを使用できます。あなたの場合、次のようになります。
struct value_t {
string s;
int i;
};
struct string_tag {};
typedef multi_index_container<
value_t,
indexed_by<
random_access<>, // this index represents insertion order
hashed_unique< tag<string_tag>, member<value_t, string, &value_t::s> >
>
> values_t;
std::vector
とともに std::tr1::unordered_map
(ハッシュテーブル)。 Boostのドキュメント for unordered_map
。ベクターを使用して挿入順序を追跡し、ハッシュテーブルを使用して頻繁に検索を行うことができます。数十万件のルックアップを行っている場合、O [log n)ルックアップとstd::map
およびO(1)ハッシュテーブルの場合は重要です。
std::vector<std::string> insertOrder;
std::tr1::unordered_map<std::string, long> myTable;
// Initialize the hash table and record insert order.
myTable["foo"] = 0;
insertOrder.Push_back("foo");
myTable["bar"] = 0;
insertOrder.Push_back("bar");
myTable["baz"] = 0;
insertOrder.Push_back("baz");
/* Increment things in myTable 100000 times */
// Print the final results.
for (int i = 0; i < insertOrder.size(); ++i)
{
const std::string &s = insertOrder[i];
std::cout << s << ' ' << myTable[s] << '\n';
}
平行を保つlist<string> insertionOrder
。
印刷するときは、listを繰り返し、mapを検索します。
each element in insertionOrder // walks in insertionOrder..
print map[ element ].second // but lookup is in map
Tessilには、MITライセンス。順序付きマップ(およびセット)の非常に素晴らしい実装があります。ここで見つけることができます。 ordered-map
地図の例
#include <iostream>
#include <string>
#include <cstdlib>
#include "ordered_map.h"
int main() {
tsl::ordered_map<char, int> map = {{'d', 1}, {'a', 2}, {'g', 3}};
map.insert({'b', 4});
map['h'] = 5;
map['e'] = 6;
map.erase('a');
// {d, 1} {g, 3} {b, 4} {h, 5} {e, 6}
for(const auto& key_value : map) {
std::cout << "{" << key_value.first << ", " << key_value.second << "}" << std::endl;
}
map.unordered_erase('b');
// Break order: {d, 1} {g, 3} {e, 6} {h, 5}
for(const auto& key_value : map) {
std::cout << "{" << key_value.first << ", " << key_value.second << "}" << std::endl;
}
}
両方のルックアップ戦略が必要な場合、2つのコンテナーになります。実際の値(vector
s)でint
を使用し、map< string, vector< T >::difference_type>
の隣に、ベクトルにインデックスを返します。
すべてを完了するには、両方を1つのクラスにカプセル化できます。
しかし、複数のインデックスを持つ boostにはコンテナがあります と考えています。
(Boostに頼らずに)必要なのは、「順序付きハッシュ」と呼ばれるものです。これは、基本的に、ハッシュと、文字列または整数キー(または両方)のリンクリストのマッシュアップです。順序付けされたハッシュは、ハッシュの絶対的なパフォーマンスで反復中の要素の順序を維持します。
私は比較的新しいC++スニペットライブラリを作成し、C++ライブラリ開発者向けのC++言語の穴と見ているものを埋めています。ここに行く:
https://github.com/cubiclesoft/cross-platform-cpp
つかむ:
templates/detachable_ordered_hash.cpp
templates/detachable_ordered_hash.h
templates/detachable_ordered_hash_util.h
ユーザーが制御するデータがハッシュに配置される場合、以下も必要になる場合があります。
security/security_csprng.cpp
security/security_csprng.h
呼び出す:
#include "templates/detachable_ordered_hash.h"
...
// The 47 is the nearest prime to a power of two
// that is close to your data size.
//
// If your brain hurts, just use the lookup table
// in 'detachable_ordered_hash.cpp'.
//
// If you don't care about some minimal memory thrashing,
// just use a value of 3. It'll auto-resize itself.
int y;
CubicleSoft::OrderedHash<int> TempHash(47);
// If you need a secure hash (many hashes are vulnerable
// to DoS attacks), pass in two randomly selected 64-bit
// integer keys. Construct with CSPRNG.
// CubicleSoft::OrderedHash<int> TempHash(47, Key1, Key2);
CubicleSoft::OrderedHashNode<int> *Node;
...
// Push() for string keys takes a pointer to the string,
// its length, and the value to store. The new node is
// pushed onto the end of the linked list and wherever it
// goes in the hash.
y = 80;
TempHash.Push("key1", 5, y++);
TempHash.Push("key22", 6, y++);
TempHash.Push("key3", 5, y++);
// Adding an integer key into the same hash just for kicks.
TempHash.Push(12345, y++);
...
// Finding a node and modifying its value.
Node = TempHash.Find("key1", 5);
Node->Value = y++;
...
Node = TempHash.FirstList();
while (Node != NULL)
{
if (Node->GetStrKey()) printf("%s => %d\n", Node->GetStrKey(), Node->Value);
else printf("%d => %d\n", (int)Node->GetIntKey(), Node->Value);
Node = Node->NextList();
}
私はこの段階でSOスレッドに遭遇し、OrderedHashのようなものが既に存在するかどうかを確認しました。大規模なライブラリに立ち寄る必要はありませんでした。がっかりしました。共有しました。
これを実装する別の方法は、map
の代わりにvector
を使用することです。このアプローチを示し、違いについて説明します。
背後で2つのマップを持つクラスを作成するだけです。
#include <map>
#include <string>
using namespace std;
class SpecialMap {
// usual stuff...
private:
int counter_;
map<int, string> insertion_order_;
map<string, int> data_;
};
次に、イテレータを適切な順序でdata_
を介してイテレータに公開できます。その方法はinsertion_order_
を反復処理し、その反復から取得する各要素に対して、data_
の値を使用してinsertion_order_
でルックアップを実行します
hash_map
を直接反復処理する必要がないため、挿入_orderにはより効率的なinsertion_order_
を使用できます。
挿入を行うには、次のようなメソッドを使用できます。
void SpecialMap::Insert(const string& key, int value) {
// This may be an over simplification... You ought to check
// if you are overwriting a value in data_ so that you can update
// insertion_order_ accordingly
insertion_order_[counter_++] = key;
data_[key] = value;
}
デザインを改善し、パフォーマンスを心配する方法はたくさんありますが、これは自分でこの機能の実装を始めるための良いスケルトンです。あなたはそれをテンプレート化することができ、実際に挿入を値としてdata_に保存して、insertion_order_のエントリを簡単に参照できるようにすることができます。しかし、これらの設計上の問題は演習として残しておきます:-)。
Update:私は、insert_order_にmap vs vectorを使用する効率について何か言うべきだと思います
削除をあまり使用しない場合は、ベクトルアプローチを使用する必要があります。マップアプローチは、挿入順序ではなく別の順序(優先順位など)をサポートしている場合に適しています。
//この男のようになります!
//これにより、挿入の複雑さがO(logN)になり、削除もO(logN)になります。
class SpecialMap {
private:
int counter_;
map<int, string> insertion_order_;
map<string, int> insertion_order_reverse_look_up; // <- for fast delete
map<string, Data> data_;
};
これは、ファイサルの答えにいくらか関連しています。地図とベクターの周りにラッパークラスを作成するだけで、簡単に同期できます。適切なカプセル化により、アクセス方法を制御できるため、ベクトルまたはマップを使用するコンテナを制御できます。これにより、Boostなどを使用する必要がなくなります。
マップでそれを行うことはできませんが、マップとベクターの2つの別個の構造を使用して、マップから削除し、ベクターから要素を見つけて削除するときに同期を保つことができます。または、map<string, pair<int,int>>
-ペアに、レコードの位置への挿入時にマップのsize()をintの値とともに格納し、印刷時に位置メンバーを使用してソートします。
つかいます boost::multi_index
マップおよびリストインデックス付き。
考慮する必要があることの1つは、使用しているデータ要素の数が少ないことです。ベクトルのみを使用する方が高速になる可能性があります。マップには、単純なベクトルよりも小さなデータセットでルックアップを行う方が高価になる可能性があるオーバーヘッドがあります。そのため、常に同じ数の要素を使用することがわかっている場合は、ベンチマークを実行して、マップとベクトルのパフォーマンスが実際に考えているとおりかどうかを確認してください。 50個の要素のみを含むベクトルでルックアップがマップとほぼ同じである場合があります。