[C++] STL map 사용법

 

STL map

맵( map )은 키( key )와 값( value )의 데이터 쌍을 저장하는 연관 컨테이너입니다.

여기서, 키( key )는 맵에서 유일한 값을 가져야 합니다. 

하지만, 그와 쌍을 이루는 값( value )은 유일할 필요가 없습니다.

 

그리고, 입력된 데이터는 내부적으로 키 값에 의해 자동으로 정렬됩니다.

정렬의 기본값은 오름차순입니다.

 

참고로, STL의 맵은 해쉬 맵( hasp map )이 아닙니다.

맵의 내부 구조는 레드-블랙 트리( Red-Black Tree )로, 맵의 키는 이 트리 구조를 탐색하는 데 사용되는 데이터입니다.

 

이 map을 사용하려면, 먼저 다음의 헤더를 포함해야 합니다.

#include <map>

 

map의 선언

map의 선언은 다음과 같습니다.

std::map<Key, T> map_name;

여기서 Key는 키( key )의 데이터 타입을 말하고, T는 값( value )의 데이터 타입을 말합니다.

 

그리고, namespace를 선언함으로써, std를 매번 명시해야 하는 번거로움을 덜 수도 있습니다.

using namespace std;

map<int, string> int_string_map;

 

map의 초기화

map은 다음과 같은 방법으로 초기화할 수 있습니다.

 

1. 초기화 리스트( initializer list )를 사용하는 방법입니다.

map<int, string> int_str_map = { {1,"korea"}, {2,"america"}, {3,"china"} };

 

2. 유니폼 초기화( uniform initialization )를 사용하는 방법입니다.

map<int, string> int_str_map { {1,"korea"}, {2,"america"}, {3,"china"}};

 

map의 기능들

맵의 기능 중에서 공통적으로 많이 쓰이는 기능을 아래와 같이 나눠서 정리하였습니다.

 

 

원소의 개수 알아내기

size

map이 저장하고 있는 원소의 개수를 반환합니다.

 

empty

map이 가지고 있는 원소의 개수가 0이면 true, 아니면 false를 반환합니다.

int main(){

    map<int, string> int_str_map { {1,"korea"}, {2,"america"}, {3,"china"}};

    cout << "size: " << int_str_map.size() << endl;

    cout << "is empty: " << int_str_map.empty();

    return 0;
}

▼출력

size: 3    
is empty: 0

 

원소 추가하기

[ ] 연산자

map_name [key] = value 형태로 { key, value } 원소를 맵에 추가합니다.

int main(){

    map<int, string> int_str_map;
    
    int_str_map[1] = "korea";   // key: 1, value: "korea"

    int_str_map[2] = "america";
    int_str_map[3] = "china";
    
    int_str_map[0];	// ok!! key: 0, value: default value
    
    int_str_map[0] = "changed";	// key: 0, value: "changed"

    return 0;
}

위의 구문 중, 다음 구문은 의외로 오류가 아닙니다.

int_str_map[0];

이 구문이 말하는 것은, { 0, 기본값 }의 원소를 추가하는 것과 같습니다.

이렇게, 맵에서 키( key )만 지정하고, 값( value )을 지정하지 않으면, 값은 기본값으로 자동 지정됩니다.

위의 경우는, 값의 데이터 타입이 문자열이므로, 빈 문자열이 기본값이 될 것입니다.

 

또한, [ ] 연산자를 사용하여, 원소를 추가하는 경우,

새로운 키가 추가되었는지, 기존의 키의 값이 변경되었는지에 대한 정보를 알 수가 없습니다.

실제로, 그다음 문장을 사용하여, 같은 키를 갖고 있는 원소의 값이 의도치 않게 변경됩니다.

int_str_map[0] = "changed";	// key: 0, value: "changed"

따라서, 연산자보다는 아래의 insert 함수 사용을 권장하고 있습니다.

 

insert

{ key, value } 원소를 맵에 추가합니다.

std::make_pair() 함수를 사용해서 원소를 입력할 수도 있습니다.

pair<iterator, bool> insert(
    const value_type& Val);

이 함수는 추가된 원소의 위치를 나타내는 iterator와, 새로운 키( key )가 추가되었는지를 나타내는 bool 값을 반환합니다.

만약, 새로운 키( key )가 추가되면 true를 반환하고, 기존의 키( key )가 존재하면 false를 반환합니다.

기존의 키가 있는 경우, 원소의 값은 변경되지 않습니다.

int main(){

    map<int, string> int_str_map;

    int_str_map.insert( { 1, "korea" } );   // key: 1, value: "korea"

    int_str_map.insert( make_pair( 2, "america") ); // key:2, value: "america"
        
    
    auto ret = int_str_map.insert( make_pair( 3, "china") );
    if ( ret.second){
        cout << "key created\n";
    }
    
    ret = int_str_map.insert( make_pair( 3, "japan") );
    if ( !ret.second){
        cout << "key exists\n";
    }
    
    return 0;
}

▼출력

key created
key exists

 

원소 접근하기

map 반복자는 map 내의 원소들에 접근하고, 원소들 간의 이동을 위해서 사용됩니다.

map의 반복자는 양방향 반복자( bidirectional iterator )입니다.

 

반복자( iterator )의 전반적인 내용은 여기에 정리해 두었습니다.

 

[C++] 반복자( Iterator )에 대한 설명과 종류

반복자( Iterator )란 무엇인가?Iterator는 컨테이너에서 원소의 위치를 표현하는, 포인터와 같은 객체입니다. 여기서, 컨테이너란 다른 객체들을 담는 집합 객체라고 할 수 있습니다. 컨테이너의

codingembers.co.kr

begin

이 함수는 map의 첫 번째 원소를 가리키는 iterator를 반환합니다.

end

이 함수는 map의 마지막 원소 다음을 가리키는 iterator를 반환합니다.

 

map의 모든 원소를 순환하고자 하면 다음과 같이 할 수 있습니다.

int main(){

    map<int, string> int_str_map;

    int_str_map.insert( { 1, "korea" } );   // key: 1, value: "korea"

    int_str_map.insert( make_pair( 2, "america") ); // key:2, value: "america"
        
    int_str_map.insert( make_pair( 3, "china") );
        
    map<int, string>::iterator iter;
    
    for( iter = int_str_map.begin(); iter != int_str_map.end(); iter++){
        cout << iter->first << ": " << iter->second << endl;
    }

    return 0;
}

▼출력

1: korea  
2: america
3: china

 

또한, 범위 기반 for 구문을 사용하여, map의 모든 원소에 접근할 수도 있습니다.

int main(){

    map<int, string> int_str_map;

    int_str_map.insert( { 1, "korea" } );   // key: 1, value: "korea"

    int_str_map.insert( make_pair( 2, "america") ); // key:2, value: "america"
        
    int_str_map.insert( make_pair( 3, "china") );
        
    for( const pair<const int, string>& i : int_str_map){
        cout << i.first << ": " << i.second << endl;
    }

    return 0;
}

▼출력

1: korea  
2: america
3: china


C++의 버전이 17 이상인 경우, 범위 기반 for 구문으로, 키( key )와 값( value )을 동시에 순환할 수도 있습니다.

for( auto& [key, value] : int_str_map){

    cout << key << ": " << value << endl;
}

▼출력

1: korea  
2: america
3: china

 

그리고, 연산자 [ ]를 사용해서, 키( key )에 대응하는 값( value )에 접근할 수도 있습니다.

int main(){

    map<int, string> int_str_map;

    int_str_map.insert( { 1, "korea" } );   // key: 1, value: "korea"

    int_str_map.insert( make_pair( 2, "america") ); // key:2, value: "america"
        
    int_str_map.insert( make_pair( 3, "china") );
        
    for( int i = 0; i < int_str_map.size(); i++){
        cout << i << ": " << int_str_map[i] << endl;
    }

    return 0;
}

▼출력

0: 
1: korea
2: america
3: china

위 코드에서 주의할 점은, 키( key )를 인덱스로 사용하면 실수가 발생할 수 있다는 점입니다.

원래의 의도는 인덱스에 대응하는 값을 출력하려는 것이지만, 키 '0'이 존재하지 않기 때문에, { 0, 기본값 }이라는 새로운 원소가 생성되는 사태가 발생합니다.

그래서, 의도치 않게 원소의 개수가 4개가 되었습니다.

 

이러한 일이 발생하지 않도록, 원소에 접근하기 전에 키( key )를 검색하는 방법을 사용해야 합니다.

 

원소 검색하기

find 

iterator find(const Key& key);

키( key )에 해당하는 원소의 위치를 반환합니다.

만약, 키에 해당하는 원소가 존재하지 않으면 end()를 반환합니다.

 

위의 이전 항목의 마지막 예제를 변경해서,

키가 존재하는 경우만 원소의 값을 출력하도록 변경하면 다음과 같습니다.

int main(){

    map<int, string> int_str_map;

    int_str_map.insert( { 1, "korea" } );   // key: 1, value: "korea"

    int_str_map.insert( make_pair( 2, "america") ); // key:2, value: "america"
        
    int_str_map.insert( make_pair( 3, "china") );
        
    for( int i = 0; i < int_str_map.size(); i++){
        
        if ( int_str_map.find(i) != int_str_map.end()){	// 키가 있다면 값을 출력
            
            cout << i << ": " << int_str_map[i] << endl;    
        }
    }

    return 0;
}

▼출력

1: korea
2: america

이제 의도치 않은 원소는 생성되지 않습니다.

그래도, 키( key )를 인덱스로 사용하면 모든 원소가 제대로 출력되고 있지 않음을 알 수 있습니다.

 

원소 삭제하기

clear

map의 모든 원소를 삭제합니다.

int main(){

    map<int, string> M;

    // 초기화 리스트( initializer list )를 이용한 원소 추가
    M.insert( { { 1, "korea" }, {2, "america"}, {3, "china"} } );   

    M.clear();  // 모든 원소 삭제

    int size = M.size();
    cout << "size: " << size;

    return 0;
}

▼출력

size: 3

erase

지정한 위치의 원소를 제거하거나, 지정한 키( key )와 일치하는 원소를 제거합니다.

int main(){

    map<int, string> M;

    // 초기화 리스트( initialization list )를 이용한 원소 추가
    M.insert( { { 1, "korea" }, {2, "america"}, {3, "china"}, {4, "japan"} } );   

    // 위치를 지정해서 삭제
    M.erase( M.begin());

    // 키를 이용해서 삭제
    M.erase( 3);

    // 남은 모든 원소 출력
    for( const auto& [ key, value] : M){
        cout << key << ": " << value << endl;
    }

    return 0;
}

▼출력

2: america
4: japan

 

시작 위치와, 종료 위치를 지정해서, 그 구간에 있는 원소들을 삭제할 수도 있습니다.

int main(){

    map<int, string> M;

    // 초기화 리스트( initialization list )를 이용한 원소 추가
    M.insert( { { 1, "korea" }, {2, "america"}, {3, "china"}, {4, "japan"} } );   

    map<int, string>::iterator iter = M.begin();
    iter++;


    // 구간을 지정해서 삭제
    M.erase( iter, M.end());

    // 남은 모든 원소 출력
    for( const auto& [ key, value] : M){
        cout << key << ": " << value << endl;
    }

    return 0;
}

▼출력

1: korea

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유