순서 맵 라이브러리는 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의 거듭제곱을 사용합니다. 좋은 성능을 위해서는 해시 테이블에 해시 함수가 잘 분산되어 있어야 합니다. 성능 문제가 발생하면 해시 함수를 확인하세요.
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
와 매우 유사한 API입니다.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->()
std::pair< std::pair<const Key, T>
const std::pair<Key, T>
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::deque
std::vector
T
유형이 예외 사항은 세부 정보를 참조하세요).
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 ( '