Библиотека упорядоченных карт предоставляет хеш-карту и хеш-набор, которые сохраняют порядок вставки аналогично 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 ( '