[STL] unordered_multimap

Yijun Jeon·2023년 8월 6일

unordered_multimap이란?

#include <unordered_map>

  • map과 다르게 중복 Key를 허락한다.
    중복 Key 가 있어도 insert 연산을 무시하지 않는다.

원소 탐색

  • [] 연산자를 사용할 수 없다.
    중복 Key를 허용하므로 map['a'] 하면 어떤 value를 리턴해야 하는지 모호하기 때문에.

  • find()함수를 사용하여 리턴할 경우 중복된 원소들 중 아무거나 리턴한다.
    즉 라이브러리마다 어떤게 리턴될지 모름. 가장 먼저 나오는 Key의 Value가 나올 수도 있고 등등… 기준이 다 다르다.

  • std::equal_range 함수 사용
    멀티 맵의 Key값을 인자로 받은 후 이 키에 대응되는

    중복 원소들의 중 첫번째 원소의 반복자(begin) : first
    중복 원소들의 중 마지막 원소의 반복자(end) : second

    std::pair형태로 리턴해준다.

auto range = m.equal_range("식빵"); // Key가 식빵인 원소들의 맨 앞 반복자와 맨 끝 반복자를 pair로 리턴한다.

for (auto itr = range.first; itr != range.second; ++itr) {  // "식빵" 키를 가진 원소들을 출력 
  std::cout << itr->first << " : " << itr->second << " " << std::endl;
}
💎출력💎

식빵 : 1
식빵 : 30
식빵 : 6

주의할 점

원소 탐색 반복 중 삭제 혹은 데이터 변경을 한다면, 순회 중인 iterator의 위치로 인해 오류가 발생할 수 있음

따라서

  • erase() 메소드가 삭제된 요소의 바로 다음 위치의 iterator를 반환
  • 요소의 데이터를 변경하기 전에는 미리 삭제 후 변경된 데이터 삽입

등의 기법으로 관리할 필요가 있음

관련 코드

#include <unordered_map>
#include <vector>
#include <iostream>
#include <cstring>
#define UMAP_ITERATOR unordered_multimap<string, Contact*>::iterator 
using namespace std;

typedef enum
{
    NAME,
    NUMBER,
    BIRTHDAY,
    EMAIL,
    MEMO
} FIELD;

typedef struct
{
    int count;
    char str[20];
} RESULT;

class Contact{
public:
    int id;
    string* data;

    Contact(int id, char* name, char* number, char* birthday, char* email, char* memo) {
        this->id = id;

        data = new string[5];
        data[NAME] = name;
        data[NUMBER] = number;
        data[BIRTHDAY] = birthday;
        data[EMAIL] = email;
        data[MEMO] = memo;
    }
    ~Contact(){
        delete []data;
    }
}; 

vector<unordered_multimap<string ,Contact*> > DB(5);
int id = 0;

void eraseByValue(Contact* contact){
    for(int field=0;field<5;field++){
        char str[20];
        strcpy(str,contact->data[field].c_str());

        pair<UMAP_ITERATOR,UMAP_ITERATOR> iters = DB[field].equal_range(str);
        UMAP_ITERATOR iter = iters.first;
        while(iter != iters.second){
            if(iter->second->id == contact->id){
                DB[field].erase(iter);
                break;
            }else iter++;
        }
    }
}

void eraseByValueAndField(Contact* contact, FIELD field){
    char str[20];
    strcpy(str,contact->data[field].c_str());

    pair<UMAP_ITERATOR,UMAP_ITERATOR> iters = DB[field].equal_range(str);
    UMAP_ITERATOR iter = iters.first;
    while(iter != iters.second){
        if(iter->second->id == contact->id){
            DB[field].erase(iter);
            break;
        }else iter++;
    }
}

void InitDB()
{
    id = 0;
    DB.clear();
    DB.resize(5);
}

void Add(char* name, char* number, char* birthday, char* email, char* memo)
{
    Contact* contact = new Contact(id++,name,number,birthday,email,memo);
    DB[NAME].insert(make_pair(name,contact));
    DB[NUMBER].insert(make_pair(number,contact));
    DB[BIRTHDAY].insert(make_pair(birthday,contact));
    DB[EMAIL].insert(make_pair(email,contact));
    DB[MEMO].insert(make_pair(memo,contact));
}

int Delete(FIELD field, char* str)
{
    int cnt = 0;

    UMAP_ITERATOR iter = DB[field].find(str);
    while(iter != DB[field].end()){
        eraseByValue(iter->second);
        iter = DB[field].find(str);
        cnt++;
    }
    
    return cnt;
}
int Change(FIELD field, char* str, FIELD changefield, char* changestr)
{
    int cnt = 0;
    pair<UMAP_ITERATOR,UMAP_ITERATOR> iters = DB[field].equal_range(str);
    UMAP_ITERATOR iter;

    // 검색 필드와 변경 필드가 다름
    if(field != changefield){
        for(iter = iters.first; iter != iters.second; iter++){
            cnt++;
            eraseByValueAndField(iter->second,changefield);
            iter->second->data[changefield] = changestr;
            DB[changefield].insert(make_pair(changestr,iter->second));
        } 
    // 검색 필드와 변경 필드가 같은 케이스   
    }else{
        // 변경할 필요가 없는 케이스
        if(strcmp(str,changestr) == 0){
            cnt = DB[field].count(str);
        }else{
            iter = DB[field].find(str);
            while(iter != DB[field].end()){
                Contact* temp = iter->second;                
                eraseByValueAndField(iter->second,changefield);
                temp->data[changefield] = changestr;
                DB[changefield].insert(make_pair(changestr,temp));
                iter = DB[field].find(str);
                cnt++;
            }
        }
    }
    
    return cnt;
}

RESULT Search(FIELD field, char* str, FIELD ret_field)
{
    RESULT result;
    result.count = 0;

    pair<UMAP_ITERATOR,UMAP_ITERATOR> iters = DB[field].equal_range(str);
    UMAP_ITERATOR iter;
    for(iter = iters.first; iter != iters.second; iter++){
        result.count++;
        strcpy(result.str,iter->second->data[ret_field].c_str());
    }

    return result;
}

References

https://cplusplus.com/reference/unordered_map/unordered_multimap/count/
https://ansohxxn.github.io/stl/map/

0개의 댓글