ordered-map 函式庫提供了一個哈希映射和一個哈希集,它們以類似於 Python 的 OrderedDict 的方式保留插入順序。迭代映射時,值將按照插入的順序傳回。
這些值連續儲存在底層結構中,即使在擦除操作之後,值之間也沒有空洞。預設情況下,此結構使用std::deque
,但也可以使用std::vector
。該結構可以透過values_container()
方法直接訪問,如果該結構是std::vector
,還提供data()
方法來輕鬆與C API互動。這提供了快速迭代,但具有 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 的冪作為其儲存桶數組的大小,以利用快速模數。為了獲得良好的性能,要求雜湊表具有分佈良好的雜湊函數。如果您遇到效能問題,請檢查您的雜湊函數。
tsl::ordered_map
匯出目標。std::unordered_map
類似,但插入速度更快並減少了內存使用量(有關詳細信息,請參閱基準測試)。Key
不同的類型的find
(例如,如果您有一個使用std::unique_ptr<foo>
作為鍵的映射,則可以使用foo*
或std::uintptr_t
作為鍵參數find
而不構造std::unique_ptr<foo>
,請參閱範例)。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!=
與順序相關。兩個具有相同值但以不同順序插入的tsl::ordered_map
比較不相等。operator*()
和operator->()
傳回一個引用和一個指向const std::pair<Key, T>
而不是std::pair<const 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
和std::deque
來說都是如此,只要類型T
不是帶有可能拋出異常的移動構造函數的僅移動類型例外,請參閱詳細資訊)。
tsl::ordered_map::erase_if
和tsl::ordered_set::erase_if
函數僅在其文件中列出的前提條件下才有保證。
要使用有序映射,只需將包含目錄新增至包含路徑即可。它是一個僅包含頭檔的庫。
如果您使用 CMake,也可以將 CMakeLists.txt 中的tsl::ordered_map
匯出目標與target_link_libraries
結合。
# 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
安裝,您也可以使用find_package(tsl-ordered-map REQUIRED)
而不是add_subdirectory
。
程式碼應適用於任何符合 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 ( '