[C/C++] map

할랑말랑·2026년 3월 13일

C/C++

목록 보기
24/45

map

키-값 쌍(key-value pair)을 저장하는 연관 컨테이너이다.
키는 자동으로 정렬되며, 각 키는 유일하다.

특징

  • 각 키는 유일해야 하며, 중복된 키를 허용하지 않는다
  • Red-Black Tree(이진 탐색 트리) 구현
  • 삽입, 삭제, 검색 연산은 O(log n)의 시간 복잡도
  • 트리 구조 유지를 위한 추가 포인터 저장으로 메모리 오버헤드가 있다

주요 멤버함수

1. 생성자 / 할당 및 초기화

#include <iostream>
#include <string>
#include <map>

int main()
{
    // --- 맵 생성 및 초기화 ---

    // 기본 생성자 : 빈 맵 생성
    std::map<int, int> map1;

    // 균일한 초기화 : {}를 사용하여 문법을 초기화하는 방식, 초기화 리스트 맵 생성
    // std::make_pair : pair 객체를 생성하는 헬퍼 함수(타입을 명시하지 않고 자동으로 타입 추론하여 pair를 생성)
    // 템플릿 함수에 pair를 전달할때, 타입 추론이 필요할때 사용
    // C++11 이후에는 중괄호 {} 초기화가 더 간결하고 명확하므로 권장
    std::map<int, std::string> map2 = { std::make_pair(5,"감자"), std::make_pair(2,"고구마") };
    std::map<int, int> map3 = { {1,2}, {10,20} };

    // 복사 생성자
    std::map<int, std::string> map4 = map2;

    // 이동 생성자 (map4는 이제 비어있거나 유효하지 않은 상태가 될 수 있음)
    std::map<int, std::string> map5 = std::move(map4);

    // 반복자 범위로 맵 생성
    std::map<int, std::string> map6(map2.begin(), map2.end());


    // --- 맵 대입 연산 ---

    std::map<int, std::string> ma = { std::make_pair(5,"감자"),std::make_pair(2,"고구마") };
    std::map<int, std::string> ma2 = { std::make_pair(5,"개"),std::make_pair(12,"고양이") };
    std::map<int, std::string> ma3_assign, ma4_assign, ma5_assign;

    // 복사 대입 연산자
    ma3_assign = ma;

    // 이동 대입 연산자 (ma는 이제 비어있거나 유효하지 않은 상태가 될 수 있음)
    ma4_assign = std::move(ma);

    // 균일한 초기화, 초기화 리스트 대입
    ma5_assign = { {1,"올이"},{122,"바둑이"} };


    // --- 사용자 정의 비교 함수를 사용한 맵 생성 ---

    // std::map(Compare comp): 사용자 정의 비교 함수로 맵 생성
    // 람다 활용
    // 문자의 길이로 정렬하는 사용자 정의 비교 함수 (긴 것부터 짧은 순으로)
    // 사용자 정의 비교 함수 : 람다 함수나 함수자를 사용하여 정렬 기준을 변경 가능
    // 템플릿 매개변수: 비교 함수를 사용할 때는 std::map<Key, Value, Compare> 형태로 타입을 지정해야 한다.
    // decltype : 괄호 안의 타입을 자동으로 추론하기 위해 사용
    auto strComp = [](const std::string& _a, const std::string& _b) { return _a.length() > _b.length(); };

    std::map<std::string, int, decltype(strComp)> map7(strComp);
    map7["A"] = 10;
    map7["BB"] = 20;
    map7["DDDGSA"] = 30;
    map7["GSEEEEEE"] = 40;

    std::cout << "사용자 정의 비교 함수로 맵 생성 map7" << std::endl;
    for (const auto& pair : map7)
    {
        std::cout << "key : " << pair.first << " , value : " << pair.second << std::endl;
    }

    return 0;
}

2. 접근자

#include <iostream>
#include <string>
#include <map>

int main()
{
    std::map<int, std::string> ma = { std::make_pair(5,"감자"),std::make_pair(2,"고구마") };
    std::map<int, std::string> ma2 = { std::make_pair(5,"개"),std::make_pair(12,"고양이") };

    try
    {
        // at(const Key& key) : 키에 해당하는 값 반환 (범위 체크)
        // 키가 존재하지 않으면 std::out_of_range 예외를 발생시킵니다.
        std::string key = ma.at(100);
    }
    catch (std::out_of_range)
    {
        std::cout << "비상" << std::endl;
    }

    // operator[](const Key& key) : 키에 해당하는 값 반환(범위 체크 없음, 없으면 자동 생성)
    // 키가 존재하지 않으면, 기본값으로 새로운 원소를 생성하고 그 참조를 반환합니다.
    std::string key2 = ma2[250];
    for (const auto& pair : ma2)
    {
        std::cout << pair.first << " , " << pair.second << std::endl;
    }

    return 0;
}

3. 조작(삽입)

#include <iostream>
#include <string>
#include <map>

int main()
{
    std::map<int, std::string> ma = { std::make_pair(5,"감자"),std::make_pair(2,"고구마") };
    std::map<int, std::string> ma2 = { std::make_pair(50,"개"),std::make_pair(55,"고양이") };

    // insert(const value_type& value)
    // 삽입값 : pair
    // 반환값 : pair<iterator, bool>
    std::pair<std::map<int, std::string>::iterator, bool> pair = ma.insert({ 10, "고양이" });
    auto pair2 = ma.insert({ 12, "독이" });

    // 반환 받은 pair<iterator, bool> second에서 삽입 성공여부 확인
    std::cout << pair.first->first << " , " << pair.second << std::endl;
    std::cout << pair2.first->first << " , " << pair2.second << std::endl;

    // insert(Iterator hint, const value_type& value)
    // Iterator hint: 이 매개변수는 삽입할 위치에 대한 힌트를 제공
    // std::map의 경우, 이 이터레이터는 삽입할 값보다 작거나 같은 값을 가리키는 위치여야한다
    // 힌트를 제공함으로써, std::map은 이터레이터가 가리키는 위치 근처에서 요소를 삽입하려고 시도한다.
    // 이는 불필요한 탐색을 줄여주고 성능을 향상시킨다.
    auto it = ma.find(3);
    ma.insert(it, { 3,"호랑이" });

    // insert(Iterator first,Iterator last) : 범위 삽입
    ma.insert(ma2.begin(), ma2.end());

    // insert(std::initializer_list) : 초기화 리스트 삽입
    ma.insert({25,"치킨"});

    // emplace(Args&&... args) : 직접 객체 생성 (객체가 생성되는 과정에서 불필요한 복사가 없기 때문에, 삽입 성능이 향상)
    ma.emplace(777,"방덕");

    // emplace_hint(Iterator hint,Args&&... args)
    // emplace + 힌트사용
    it = ma.find(777);
    ma.emplace_hint(it,776,"불덕");

    std::cout << "ma" << std::endl;
    for (const auto& pair : ma)
    {
        std::cout << pair.first << " , " << pair.second << std::endl;
    }

    std::cout << "ma2" << std::endl;
    for (const auto& pair : ma2)
    {
        std::cout << pair.first << " , " << pair.second << std::endl;
    }

    return 0;
}

4. 조작(삭제)

#include <iostream>
#include <string>
#include <map>

int main()
{
    std::map<int, std::string> ma = { std::make_pair(5,"감자"),std::make_pair(2,"고구마") };
    std::map<int, std::string> ma2 = { std::make_pair(50,"개"),std::make_pair(55,"고양이") };
    std::map<int, std::string> ma3 = { std::make_pair(23,"콜라"),std::make_pair(15,"사이다") };

    // erase(const Key& key) : 키로 삭제, 반환값은 삭제된 요소 개수
    int count = ma.erase(5);
    std::cout << "삭제된 요소 개수 : " << count << std::endl;

    // erase(Iterator pos) : 반복자 위치의 요소 삭제
    // erase가 반복자를 반환하는 것은 연속적인 삭제 작업, 성능 최적화, 코드의 가독성을 높이는 데 유용
    auto pair = ma.erase(ma.begin());
    if (pair == ma.end())
    {
        std::cout << "비어있습니다" << std::endl;
    }

    // erase(Iterator first, Iterator last) : 범위 삭제
    pair = ma2.erase(ma2.begin(), ma2.end());

    // clear() : 모든 요소 삭제
    ma3.clear();

    std::cout << "ma" << std::endl;
    for (const auto& pair : ma)
    {
        std::cout << pair.first << " , " << pair.second << std::endl;
    }

    std::cout << "ma2" << std::endl;
    for (const auto& pair : ma2)
    {
        std::cout << pair.first << " , " << pair.second << std::endl;
    }

    std::cout << "ma3" << std::endl;
    for (const auto& pair : ma3)
    {
        std::cout << pair.first << " , " << pair.second << std::endl;
    }

    return 0;
}

5. 검색

#include <iostream>
#include <string>
#include <map>

int main()
{
    std::map<int, std::string> ma = { std::make_pair(5,"감자"),std::make_pair(2,"고구마") };
    std::map<int, std::string> ma2 = { std::make_pair(50,"개"),std::make_pair(55,"고양이") };

    // find(const Key& key) : 키 검색
    std::map<int, std::string>::iterator it = ma.find(1);
    if (it != ma.end())
    {
        std::cout << it->first << " , " << it->second << std::endl;
    }
    else
    {
        std::cout << "해당하는 요소가 없습니다." << std::endl;
    }

    // count(const Key& key) : 키의 개수 반환
    int count = ma.count(5);
    std::cout << "키의 개수 : " << count << std::endl;

    // lower_bound(const Key& key) : 키 이상인 첫 번째 요소
    it = ma.lower_bound(2);
    if (it != ma.end())
    {
        std::cout << it->first << " , " << it->second << std::endl;
    }

    // upper_bound(const Key& key) : 키 초과인 첫 번째 요소
    it = ma.upper_bound(2);
    if (it != ma.end())
    {
        std::cout << it->first << " , " << it->second << std::endl;
    }

    // equal_range(const Key& key) : 키와 같은 범위 반환
    // 반환값 : std::pair<iterator, iterator>
    //
    // 첫 번째 반복자는 주어진 키와 같은 값이 처음 나타나는 위치를 가리키고
    // 두 번째 반복자는 그 값이 마지막으로 나타나는 다음 위치를 가리킨다.
    auto range = ma.equal_range(2);
    // std::pair<std::map<int,std::string>::iterator,std::map<int,std::string>::iterator> = ma.equal_range(2);

    for (auto it = range.first; it != range.second; it++)
    {
        std::cout << it->first << " , " << it->second << std::endl;
    }

    return 0;
}

6. 길이 및 용량

#include <iostream>
#include <string>
#include <map>

int main()
{
    std::map<int, std::string> ma = { std::make_pair(5,"감자"),std::make_pair(2,"고구마") };
    std::map<int, std::string> ma2 = { std::make_pair(50,"개"),std::make_pair(55,"고양이") };

    // size() : 맵에 저장된 요소의 개수 반환
    std::cout << "ma2 size : " << ma2.size() << std::endl;

    // empty() : 맵이 비어있는지 여부
    std::cout << "ma empty : " << ma.empty() << std::endl;

    // max_size() : 맵이 저장할 수 있는 최대 요소 개수
    std::cout << "ma2 empty : " << ma2.max_size() << std::endl;

    return 0;
}

참고

  • max_size()
    컨테이너의 용량을 확인하거나, 컨테이너에 추가할 수 있는 요소의 수를 예측할 때 유용하다.
    반환된 최대 크기는 이론적인 최대치일 수 있으며, 실제로는 메모리 부족 등의 이유로 더 적은 요소만 저장할 수 있을 수 있다.

7. 반복자

#include <iostream>
#include <string>
#include <map>

int main()
{
    std::map<int, std::string> ma = { std::make_pair(5,"감자"),std::make_pair(2,"고구마") };
    std::map<int, std::string> ma2 = { std::make_pair(50,"개"),std::make_pair(55,"고양이") };

    // begin() : 첫 번째 요소를 가리키는 반복자 반환
    std::map<int, std::string>::iterator it1 = ma.begin();

    // end() : 마지막 요소의 다음을 가리키는 반복자 반환
    std::map<int, std::string>::iterator it2 = ma.end();

    // rbegin() : 역방향 첫 번째 요소를 가리키는 반복자 반환
    std::map<int, std::string>::reverse_iterator it3 = ma.rbegin();

    // rend() : 역방향 마지막 요소의 다음을 가리키는 반복자 반환
    std::map<int, std::string>::reverse_iterator it4 = ma.rend();

    // cbegin() : 첫 번째 요소를 가리키는 const 반복자 반환
    std::map<int, std::string>::const_iterator it5 = ma.cbegin();

    // cend() : 마지막 요소의 다음을 가리키는 const 반복자 반환
    std::map<int, std::string>::const_iterator it6 = ma.cbegin();

    // crbegin() : 역방향 첫 번째 요소를 가리키는 const 반복자 반환
    std::map<int, std::string>::const_reverse_iterator it7 = ma.crbegin();

    // crend() : 역방향 마지막 요소의 다음을 가리키는 const 반복자 반환
    std::map<int, std::string>::const_reverse_iterator it8 = ma.crend();

    return 0;
}

8. 연산자

#include <iostream>
#include <string>
#include <map>

int main()
{
    std::map<int, std::string> ma = { std::make_pair(5,"감자"),std::make_pair(2,"고구마") };
    std::map<int, std::string> ma2 = { std::make_pair(50,"개"),std::make_pair(55,"고양이") };

    // operator== : 두 맵이 같은지 비교 (크기와 모든 요소)
    // 두 맵의 크기가 같고, 모든 요소의 키와 값이 순서대로 정확히 일치해야 true를 반환합니다.
    if (ma == ma2)
    {
        std::cout << "ma == ma2" << std::endl;
    }

    // operator!= : 두 맵이 다른지 비교
    // 크기가 다르거나, 하나라도 다른 요소를 가지고 있으면 true를 반환합니다.
    if (ma != ma2)
    {
        std::cout << "ma != ma2" << std::endl;
    }

    // operator< : 사전식 순서(lexicographical)로 작은지 비교
    // 두 맵의 첫 요소부터 순서대로 비교하여, 처음으로 다른 요소가 나타나는 지점에서
    // 키-값 쌍을 비교합니다. ma의 첫 키(2)가 ma2의 첫 키(50)보다 작으므로 true가 됩니다.
    if (ma < ma2)
    {
        std::cout << "ma < ma2" << std::endl;
    }

    // operator> : 사전식 순서로 큰지 비교
    if (ma > ma2)
    {
        std::cout << "ma > ma2" << std::endl;
    }

    // operator<= : 사전식 순서로 작거나 같은지 비교
    // 작거나, 완전히 같으면 true를 반환합니다.
    if (ma <= ma2)
    {
        std::cout << "ma <= ma2" << std::endl;
    }

    // operator>= : 사전식 순서로 크거나 같은지 비교
    // 크거나, 완전히 같으면 true를 반환합니다.
    if (ma >= ma2)
    {
        std::cout << "ma >= ma2" << std::endl;
    }

    return 0;
}

참고

  • == 연산자는 두 객체가 같다고 판단하기 위해서 모든 요소를 확인한다.
    이 때, 요소의 순서는 중요하지 않는다.
  • ex) {{1,1} {2,2}, {3,3}} 과 {{3,3}, {2,2}, {1,1}}는 같은 요소를 가지고 있으므로 true ( 요소의 순서는 관계없다)
  • 비교 연산자(<, >, <=, >=)는 요소의 순서에따라 달라진다. 첫 번째 요소부터 마지막 요소까지 사전식으로 비교한다.(요소의 순서가 중요)

9. 기타

#include <iostream>
#include <string>
#include <map>

int main()
{
    // 맵 초기화
    std::map<int, std::string> ma = { std::make_pair(5,"감자"),std::make_pair(2,"고구마") };
    std::map<int, std::string> ma2 = { std::make_pair(50,"개"),std::make_pair(55,"고양이") };

    // --- 첫 번째 코드: swap ---
    std::cout << "swap 전" << std::endl;
    std::cout << "<< ma >>" << std::endl;
    for (const auto& pair : ma)
    {
        std::cout << pair.first << " , " << pair.second << std::endl;
    }
    std::cout << "<< ma2 >>" << std::endl;
    for (const auto& pair : ma2)
    {
        std::cout << pair.first << " , " << pair.second << std::endl;
    }

    // swap(std::map& other)
    // 두 객체의 값을 효율적으로 교환하는 데 사용
    ma.swap(ma2);
    std::cout << "\nswap 후" << std::endl;
    std::cout << "<< ma >>" << std::endl;
    for (const auto& pair : ma)
    {
        std::cout << pair.first << " , " << pair.second << std::endl;
    }
    std::cout << "<< ma2 >>" << std::endl;
    for (const auto& pair : ma2)
    {
        std::cout << pair.first << " , " << pair.second << std::endl;
    }

    std::cout << "\n----------------------------------------\n" << std::endl;

    // --- 두 번째 코드: key_comp, value_comp, get_allocator ---

    // key_comp() : 컨테이너의 키를 비교하는 데 사용되는 비교 함수 객체 반환
    // 두 키를 비교하여 어떤 키가 더 작은지 판단
    // 사용자가 정의 기준을 사용할 수 있으며, 기본적으로는 operator<를 사용
    auto comp1 = ma.key_comp();
    // ma의 첫 키(50)와 5를 비교합니다. 5가 50보다 작으므로 true(1)가 출력됩니다.
    std::cout << "key_comp : " << comp1(ma.begin()->first, 5) << std::endl;

    // value_comp() : 컨테이너의 값을 비교하는 데 사용되는 비교 함수 객체 반환
    // 두 값의 크기를 비교하여 어떤 값이 더 작은지를 판단
    // 키와 값이 모두 포함된 std::pair<const Key, T> 형태로 값을 비교
    auto comp2 = ma2.value_comp();
    for (const auto& pair : ma) // ma2는 현재 {2, "고구마"}, {5, "감자"}
    {
        // ma2의 각 pair와 {1, "노랑감"}을 비교합니다.
        // 키 값을 기준으로 비교하므로 항상 false(0)가 출력됩니다.
        std::cout << "value_comp : " << comp2(pair, { 1,"노랑감" }) << std::endl;
    }

    // get_allocator() : 메모리 할당자를 반환하는 함수
    // 이 함수는 메모리 관리와 관련된 작업에 사용
    auto alloc = ma.get_allocator();
    auto p = alloc.allocate(1);
    alloc.deallocate(p, 1);

    return 0;
}

참고

  • get_allocator는 컨테이너가 사용하는 메모리 할당자를 반환하며, 메모리 할당 및 해제 작업을 직접 수행할 수 있게 해준다.
    이는 메모리 관리 최적화 및 사용자 정의 메모리 전략을 구현하는데 유용하다.

0개의 댓글