順序付きマップ ライブラリは、Python の OrderedDict と同様の方法で挿入順序を保持するハッシュ マップとハッシュ セットを提供します。マップを反復処理すると、値は挿入されたときと同じ順序で返されます。
値は下層の構造に連続して保存され、消去操作後でも値の間に穴が生じることはありません。デフォルトでは、この構造にはstd::deque
が使用されますが、 std::vector
使用することも可能です。この構造体には、 values_container()
メソッドを通じて直接アクセスできます。構造体がstd::vector
の場合、C API と簡単に対話できるようにdata()
メソッドも提供されます。これにより高速な反復が可能になりますが、O(bucket_count) 個の消去操作が必要になるという欠点があります。 O(1) pop_back()
と O(1) unordered_erase()
関数が利用可能です。順序付け消去を頻繁に使用する場合は、別のデータ構造を推奨します。
ハッシュの衝突を解決するために、ライブラリは後方シフト削除による線形ロビン フード プローブを使用します。
このライブラリは、一意の値を持つstd::deque/std::vector
と同様の動作を提供しますが、検索の平均時間計算量は O(1)、挿入の償却時間計算量は O(1) です。これには、メモリ使用量が少し増えるという代償が伴います (デフォルトではバケットあたり 8 バイト)。
tsl::ordered_map
とtsl::ordered_set
という 2 つのクラスが提供されています。
注: ライブラリは、高速モジュロを利用するために、バケット配列のサイズに 2 の累乗を使用します。良好なパフォーマンスを得るには、ハッシュ テーブルに適切に分散されたハッシュ関数が必要です。パフォーマンスの問題が発生した場合は、ハッシュ関数を確認してください。
tsl::ordered_map
ターゲットを使用することもできます。std::unordered_map
と同様ですが、挿入が高速になり、メモリ使用量が削減されます (詳細についてはベンチマークを参照)。Key
とは異なる型でのfind
の使用が可能になります (たとえば、キーとしてstd::unique_ptr<foo>
使用するマップがある場合、キー パラメーターとしてfoo*
またはstd::uintptr_t
を使用できます)。 std::unique_ptr<foo>
を構築せずにfind
。例を参照してください)。precalculated_hash
パラメータを参照)。serialize/deserialize
メソッドを参照してください)。-fno-exceptions
オプションを使用するか、MSVC では/EH
オプションを使用しないか、単にTSL_NO_EXCEPTIONS
定義することによって)。 std::terminate
例外が無効な場合にthrow
命令の代わりに使用されます。std::unordered_map
およびstd::unordered_set
によく似ています。std::unordered_map
との違いtsl::ordered_map
std::unordered_map
と同様のインターフェースを持とうとしますが、いくつかの違いが存在します。
RandomAccessIterator
です。std::vector
およびstd::deque
に近い方法で動作します (詳細については API を参照)。 std::vector
ValueTypeContainer
として使用する場合、 reserve()
使用してスペースを事前に割り当て、挿入時のイテレータの無効化を回避できます。erase()
操作が遅く、O(bucket_count) の複雑さがあります。より高速な O(1) バージョンのunordered_erase()
が存在しますが、挿入順序が壊れます (詳細については API を参照)。 O(1) pop_back()
も使用できます。operator==
とoperator!=
は順序に依存します。同じ値を持つ 2 つのtsl::ordered_map
異なる順序で挿入された場合、等しく比較されません。operator*()
とoperator->()
は、 std::pair<const Key, T>
const std::pair<Key, T>
への参照とポインタを返し、値T
変更できなくなります。値を変更するには、反復子のvalue()
メソッドを呼び出して変更可能な参照を取得する必要があります。例: tsl::ordered_map< int , int > map = {{ 1 , 1 }, { 2 , 1 }, { 3 , 1 }};
for ( auto it = map.begin(); it != map.end(); ++it) {
// it->second = 2; // Illegal
it. value () = 2 ; // Ok
}
IndexType
クラス テンプレート パラメーターを通じて発生させることができます。詳細については API を確認してください。bucket_size
、 bucket
など) はサポートされていません。スレッド安全性の保証はstd::unordered_map
と同じです (つまり、ライターなしで複数の同時リーダーを持つことが可能です)。
これらの違いは、 std::unordered_set
とtsl::ordered_set
の間にも当てはまります。
特に明記されていない限り、関数には強力な例外保証があります。詳細を参照してください。この保証が提供されない場合を以下に示します。
この保証は、 ValueContainer::emplace_back
強力な例外保証がある場合にのみ提供されます (これはstd::vector
型T
がstd::deque
例外、詳細を参照してください)。
tsl::ordered_map::erase_if
とtsl::ordered_set::erase_if
関数は、ドキュメントに記載されている前提条件の下でのみ保証されます。
順序付きマップを使用するには、インクルード ディレクトリをインクルード パスに追加するだけです。ヘッダーのみのライブラリです。
CMake を使用する場合は、 target_link_libraries
を使用して CMakeLists.txt からエクスポートされたtsl::ordered_map
ターゲットを使用することもできます。
# Example where the ordered-map project is stored in a third-party directory
add_subdirectory (third-party/ordered-map)
target_link_libraries (your_target PRIVATE tsl::ordered_map)
プロジェクトがmake install
によってインストールされている場合は、 add_subdirectory
の代わりにfind_package(tsl-ordered-map REQUIRED)
を使用することもできます。
コードは C++11 標準に準拠したコンパイラで動作する必要があり、GCC 4.8.4、Clang 3.5.0、および Visual Studio 2015 でテストされています。
テストを実行するには、Boost Test ライブラリと CMake が必要です。
git clone https://github.com/Tessil/ordered-map.git
cd ordered-map/tests
mkdir build
cd build
cmake ..
cmake --build .
./tsl_ordered_map_tests
API はここにあります。
# include < iostream >
# include < string >
# include < cstdlib >
# include < tsl/ordered_map.h >
# include < tsl/ordered_set.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;
}
for ( auto it = map. begin (); it != map. end (); ++it) {
// it->second += 2; // Not valid.
it. value () += 2 ;
}
if (map. find ( ' d ' ) != map. end ()) {
std::cout << " Found 'd'. " << std::endl;
}
const std:: size_t precalculated_hash = std::hash< char >()( ' d ' );
// If we already know the hash beforehand, we can pass it as argument to speed-up the lookup.
if (map. find ( ' d ' , precalculated_hash) != map. end ()) {
std::cout << " Found 'd' with hash " << precalculated_hash << " . " << std::endl;
}
tsl::ordered_set< char , std::hash< char >, std::equal_to< char >,
std::allocator< char >, std::vector< char >> set;
set. reserve ( 6 );
set = { ' 3 ' , ' 4 ' , ' 9 ' , ' 2 ' };
set. erase ( ' 2 ' );
set. insert ( ' 1 ' );
set. insert ( '