La biblioteca de mapas ordenados proporciona un mapa hash y un conjunto hash que preserva el orden de inserción de una manera similar a OrderedDict de Python. Al iterar sobre el mapa, los valores se devolverán en el mismo orden en que se insertaron.
Los valores se almacenan de forma contigua en una estructura subyacente, sin espacios entre valores incluso después de una operación de borrado. De forma predeterminada, se usa un std::deque
para esta estructura, pero también es posible usar un std::vector
. Se puede acceder directamente a esta estructura a través del método values_container()
y si la estructura es std::vector
, también se proporciona un método data()
para interactuar fácilmente con las API de C. Esto proporciona una iteración rápida pero con el inconveniente de una operación de borrado O(bucket_count). Están disponibles las funciones O(1) pop_back()
y O(1) unordered_erase()
. Si se utiliza con frecuencia el borrado ordenado, se recomienda otra estructura de datos.
Para resolver colisiones en hashes, la biblioteca utiliza un sondeo lineal de Robin Hood con eliminación por desplazamiento hacia atrás.
La biblioteca proporciona un comportamiento similar a std::deque/std::vector
con valores únicos pero con una complejidad de tiempo promedio de O(1) para búsquedas y una complejidad de tiempo amortizada de O(1) para inserciones. Esto tiene el precio de una huella de memoria un poco mayor (8 bytes por depósito de forma predeterminada).
Se proporcionan dos clases: tsl::ordered_map
y tsl::ordered_set
.
Nota : La biblioteca utiliza una potencia de dos para el tamaño de su matriz de depósitos para aprovechar el módulo rápido. Para obtener un buen rendimiento, es necesario que la tabla hash tenga una función hash bien distribuida. Si encuentra problemas de rendimiento, verifique su función hash.
tsl::ordered_map
desde CMakeLists.txt.std::unordered_map
pero con inserciones más rápidas y uso de memoria reducido (consulte el punto de referencia para obtener más detalles).find
con un tipo diferente a Key
(por ejemplo, si tiene un mapa que usa std::unique_ptr<foo>
como clave, puede usar foo*
o std::uintptr_t
como parámetro clave para find
sin construir un std::unique_ptr<foo>
, ver ejemplo).precalculated_hash
en API).serialize/deserialize
en la API para obtener más detalles).-fno-exceptions
en Clang y GCC, sin una opción /EH
en MSVC o simplemente definiendo TSL_NO_EXCEPTIONS
). std::terminate
se usa en reemplazo de la instrucción throw
cuando las excepciones están deshabilitadas.std::unordered_map
y std::unordered_set
.std::unordered_map
tsl::ordered_map
intenta tener una interfaz similar a std::unordered_map
, pero existen algunas diferencias.
RandomAccessIterator
.std::vector
y std::deque
(consulte la API para obtener más detalles). Si usa std::vector
como ValueTypeContainer
, puede usar reserve()
para preasignar algo de espacio y evitar la invalidación de los iteradores al insertar.erase()
, tiene una complejidad de O (bucket_count). Existe una versión O(1) más rápida, unordered_erase()
, pero rompe el orden de inserción (consulte la API para obtener más detalles). También está disponible un O(1) pop_back()
.operator==
y operator!=
dependen del orden. Dos tsl::ordered_map
con los mismos valores pero insertados en un orden diferente no se comparan iguales.operator*()
y operator->()
devuelven una referencia y un puntero a const std::pair<Key, T>
en lugar de std::pair<const Key, T>
lo que hace que el valor T
no sea modificable. Para modificar el valor, debe llamar al método value()
del iterador para obtener una referencia mutable. Ejemplo: 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
; consulte la API para obtener más detalles.bucket_size
, bucket
, ...). La garantía de seguridad de subprocesos es la misma que std::unordered_map
(es decir, es posible tener varios lectores simultáneos sin ningún escritor).
Estas diferencias también se aplican entre std::unordered_set
y tsl::ordered_set
.
Si no se menciona lo contrario, las funciones tienen una sólida garantía de excepción; consulte los detalles. A continuación enumeramos los casos en los que no se proporciona esta garantía.
La garantía solo se proporciona si ValueContainer::emplace_back
tiene una fuerte garantía de excepción (lo cual es cierto para std::vector
y std::deque
siempre que el tipo T
no sea un tipo de solo movimiento con un constructor de movimiento que pueda generar un excepción, ver detalles).
Las funciones tsl::ordered_map::erase_if
y tsl::ordered_set::erase_if
solo tienen garantía bajo las condiciones previas enumeradas en su documentación.
Para usar el mapa ordenado, simplemente agregue el directorio de inclusión a su ruta de inclusión. Es una biblioteca de solo encabezados .
Si usa CMake, también puede usar el destino exportado tsl::ordered_map
desde CMakeLists.txt con 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)
Si el proyecto se instaló mediante make install
, también puede usar find_package(tsl-ordered-map REQUIRED)
en lugar de add_subdirectory
.
El código debería funcionar con cualquier compilador compatible con el estándar C++ 11 y ha sido probado con GCC 4.8.4, Clang 3.5.0 y Visual Studio 2015.
Para ejecutar las pruebas necesitará la biblioteca Boost Test y 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
La API se puede encontrar aquí.
# 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 ( '