
Библиотека упорядоченных карт предоставляет хеш-карту и хеш-набор, которые сохраняют порядок вставки аналогично OrderedDict в Python. При переборе карты значения будут возвращены в том же порядке, в котором они были вставлены.
Значения хранятся в базовой структуре последовательно, без пробелов между значениями даже после операции стирания. По умолчанию для этой структуры используется std::deque , но также можно использовать std::vector . Эта структура доступна напрямую через values_container() , а если структура представляет собой std::vector , также предоставляется метод data() для простого взаимодействия с API C. Это обеспечивает быструю итерацию, но с недостатком операции стирания O(bucket_count). Доступны функции pop_back() O(1) и unordered_erase() O(1). Если часто используется упорядоченное стирание, рекомендуется использовать другую структуру данных.
Для разрешения коллизий хэшей библиотека использует линейное зондирование Робин Гуда с удалением обратного сдвига.
Библиотека обеспечивает поведение, аналогичное std::deque/std::vector с уникальными значениями, но со средней временной сложностью O(1) для поиска и амортизированной временной сложностью O(1) для вставок. За это приходится платить немного большим объемом памяти (по умолчанию 8 байт на сегмент).
Предоставляются два класса: tsl::ordered_map и tsl::ordered_set .
Примечание . Библиотека использует степень двойки для размера своего массива сегментов, чтобы воспользоваться преимуществами быстрого модуля. Для хорошей производительности требуется, чтобы хеш-таблица имела хорошо распределенную хеш-функцию. Если у вас возникли проблемы с производительностью, проверьте свою хеш-функцию.
tsl::ordered_map из файла CMakeLists.txt.std::unordered_map , но с более быстрыми вставками и меньшим использованием памяти (подробности см. в тесте).find с типом, отличным от Key (например, если у вас есть карта, которая использует std::unique_ptr<foo> в качестве ключа, вы можете использовать foo* или std::uintptr_t в качестве ключевого параметра для find без создания std::unique_ptr<foo> , см. пример).precalculated_hash в API).serialize/deserialize в API).-fno-exceptions в Clang и GCC, без опции /EH в MSVC или просто путем определения 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). Также доступен pop_back() O(1).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, вы также можете использовать экспортированную цель tsl::ordered_map из CMakeLists.txt с помощью 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 ( '