메모내용

TSet

TSet 요약

TSet "집합" 순서가 중요하지 않은 data를 저장할때 사용하며, data 자체를 key 값으로 사용하는 탐색이 빠른 고속 동적 배열.

사용

세트 생성 및 채우기

                    
                        // 빈 세트가 생성된다.
                        TSet<FString> FruitSet; 

                        // Element 추가
                        FruitSet.Add(TEXT("Banana"));
                        FruitSet.Add(TEXT("Grapefruit"));
                        FruitSet.Add(TEXT("Pineapple"));
                        // FruitSet == {"Banana", "Grapefruit", "Pineapple"}

                        // 중복시 그대로
                        FruitSet.Add(TEXT("Pineapple"));
                        // FruitSet == {"Banana", "Grapefruit", "Pineapple"}

                        // Add 대신 Emplace 사용시 복사 없이 데이터 추가
                        FruitSet.Emplace(TEXT("Orange"));
                        // FruitSet == {"Banana", "Grapefruit", "Pineapple","Orange"}


                        // Append 함수로 다른 TSet 와 병합이 가능하다.
                        
                        TSet<FString> FruitSet2; 

                        // Element 추가
                        FruitSet.Add(TEXT("Kiwi"));
                        FruitSet.Add(TEXT("Melon"));
                        FruitSet.Add(TEXT("Mango"));
                        FruitSet.Add(TEXT("Orange"));
                        // FruitSet2 == {"Kiwi","Melon","Mango","Orange"}

                        // 병합
                        FruitSet.Append(FruitSet2);
                        // FruitSet == {"Banana", "Grapefruit", "Pineapple","Orange","Kiwi","Melon","Mango"}
                    
                

Iteration 이터레이션(반복)

For 를 사용할 수도 있고 Iterator 를 사용할 수도 있다.

                    
                        FruitSet; // {"Banana", "Grapefruit", "Pineapple","Orange","Kiwi","Melon","Mango"}

                        // For 반복
                        for(auto& Elem : FruitSet)
                        {
                            Elem // "Bnana", "Grapefruit", ...
                        }

                        // CreateIterator & CreateConstIterator 반복
                        for(auto It = FruitSet.CreateConstIterator(); It; ++It)
                        {
                            *It // "Banana", "Grapefruit", ...
                        }
                    
                

Query 쿼리(질문)

                    
                        FruitSet; // {"Banana", "Grapefruit", "Pineapple","Orange","Kiwi","Melon","Mango"}

                        int32 Count = FruitSet.Num(); // 8

                        bool bHasBanana = FruitSet.Contains(TEXT("Banana")); // Banana 가 있는지 빠르게 검색

                        // FSetElementId 라는 구조체를 사용하여 키 인덱스를 찾을 수 있다.
                        // TSet 에서 내가 원하는 값이 몇번의 Index 를 들고 있는지 모르니까, FSetElementId 와 Index 함수를 이용해 찾을 수 있다.
                        FSetElementId BananaIndex = FruitSet.Index(TEXT("Banana")); // Banana 의 인덱스 를 찾기
                        FString str = FruitSet[BananaIndex]; // "Banana"

                        // 만약 없는 값의 인덱스라면
                        // LemonIndex 는 INDEX_NONE , -1 이 된다.
                        FSetElementId LemonIndex = FruitSet.Index(TEXT("Lemon"));
                        FString str = FruitSet[LemonIndex]; // assert!!

                        // 세트에 키가 있는지 확실하지 않은경우, 즉 위 방법은 위험하니
                        // Contains 함수 그리고 operator[] 를 사용해서 검사 할 수 있는데, 이 동작은 같은 키를 조회하기 위해서 두번이나 검사합니다.
                        // Find 함수는 한번에 이 목적을 수행할 수 있습니다.
                        // Find 함수는 값이 있으면 Element* 를, 없으면 nullptr 을 반환합니다.
                        FString* PtrBanana = FruitSet.Find(TEXT("Banana")); // *PtrBanana = "Banana";
                        FString* PtrLemon = FruitSet.Find(TEXT("Lemon")); // PtrLemon = nullptr;

                        // TArray 로 반환할 수도 있다.
                        TArray<FString> FruitArray = FruitSet.Array();
                    
                

Remove 제거

                    

                        FruitSet; // {"Banana", "Grapefruit", "Pineapple","Orange","Kiwi","Melon","Mango"} ;

                        // Remove 함수를 사용해 제거 할 수 있는데,
                        // Index 를 붙여 제거할 수도 있고(반복문에서만 추천), 키 를 넣을 수도 있습니다.
                        // 그리고 제거된 Element 수를 반환합니다. 키가 없다면 0 을 반환합니다.
                        // 중복키를 지원하도록 커스텀 된 경우, 중복키 모두 삭제 합니다.

                        int32 removedCount = 0;
                        
                        removedCount = FruitSet.Remove(0); // {"Grapefruit", "Pineapple","Orange","Kiwi","Melon","Mango"}; // 1

                        removedCount = FruitSet.Remove(TEXT("Pineapple")); // {"Grapefruit","Orange","Kiwi","Melon","Mango"} // 1

                        removedCount = FruitSet.Remove(TEXT("Lemon")); // {"Grapefruit","Orange","Kiwi","Melon","Mango"} // 0

                        // 모두 삭제
                        FSet<FString> FruitSetCopy = FruitSet; 
                        
                        FruitSetCopy; // {"Grapefruit","Orange","Kiwi","Melon","Mango"} ;

                        FruitSetCopy.Empty(); // {};

                    
                

Sort 소팅(정렬)

                    
                        FruitSet; // {"Grapefruit","Orange","Kiwi","Melon","Mango"} ;

                        FruitSet.Sort([](const FString& A , const FString& B){
                            return A > B; // 알파벳 역순으로 정렬
                        })

                        FruitSet; // {"Orange", "Melone", "Mango","Kiwi", "Grapefruit"};

                        FruitSet.Sort([](const FString&A , const FString& B){
                            return A.Len() < B.Len(); // 길이 오름차순 정렬
                        })

                        FruitSet; // {"Kiwi", "Mango", "Orange", "Melone", "Grapefruit"};

                    
                

Operator 연산자

TArray 와 마찬가지로, TSet 은 값 유형이므로, 할당 연산자를 통해 복사할 수 있으며, Tset 을 복사하면 깊은 복사 되어 , 새 TSet 은 별도 사본을 갖습니다.

                    
                        FruitSet; // {"Kiwi", "Mango", "Orange", "Melone", "Grapefruit"};

                        TSet<FString> NewSet = FruitSet;
                        NewSet.Add(TEXT("Apple"));
                        NewSet.Remove(TEXT("Orange"));

                        FruitSet; // {"Kiwi", "Mango", "Orange", "Melone", "Grapefruit"};
                        NewSet; // {"Kiwi", "Mango", "Melone", "Grapefruit","Apple"};
                    
                

TSet 의 내부구조

TSet 은 내부적으로 동적 가변 배열을 가지지만, 중간중간에 데이터가 빠져있을 수 있다. 내부적으로 해시테이블이 구성되어 있기 때문에 빠르게 구성되어 있다는 점이 장점이다. 그리고 데이터를 추가할 때 비어있는 부분부터 빠르게 채워 넣는 방식으로 작동하는데, 배열의 끝에서부터 채워 넣는다. 배열의 끝에서 채워 넣지만 순서를 다 파악하기에는 어려우며 신뢰할 수 없다, 순서를 고려하지 않을 목적으로 사용하는것이 좋다.

공식문서

TSet 는 TMap 과 TMultiMap 과 비슷하지만 중요한 차이점이 있다고 한다. 독립된 키로 데이터 값을 연결하기 보다. TSet 은 값 자체를 키로 사용하며, 이때 Element 의 값을 평가하는 virtual override 함수를 사용합니다. TSet 은 Element 추가, 검색, 제거 가 매우 빠르며, 기본적으로 TSet 은 중복키를 지원하지 않지만, 템플릿 파라미터로 사용할 수 있다. TSet 은 순서가 중요하지 않은 상황 에서 고유 Element 를 저장하는데 사용되는 고속 컨테이너 클래스 TSet 의 내부 Element 는 모두 같은 Type 이다 (동질성 컨테이너) TSet 은 값 유형 이기도 하며, 일반적인 복사, 할당, 소멸자 연산뿐 아니라 TSet 가 소멸되면 내부 Element 도 같이 소멸 되도록 하는 강 오너십 (strong ownership) 도 지원합니다. 내부적으로 Hash 를 사용하며, KeyFuncs 라는 템플릿 Parameter 가 제공 된 경우 커스텀도 가능. (KeyFuncs 를 작성하려면, DefaultKeyFuncs 구조체를 확장하면 된다.) 값 비교에는 템플릿으로 입력된 Type 의 operator == 를 사용. 해싱에는 GetTypeHash 를 사용. (언리얼의 FString 이나 언리얼 오브젝트는 기본적으로 가지고 있으나, 따로 커스텀 자료형을 만들었다면 GetTypeHash 함수를 만들어 줘야 한다.) 중복키를 허용하지 않는다. 세트의 데이터 구조는 희소 배열로, 엘리먼트 사이 간극을 효율적으로 지원하는 배열이다. (Sparse Matrix : 희소행렬)

STL 의 set 과 Unreal TSet 비교

STL set 과 언리얼 TSet 의 활용 방법은 서로 다르기 때문에 주의 해야 하며. STL 의 unorderd_set 과 유사하게 동작하지만 동일하지 않다. TSet 은 중복없는 데이터 집합을 구축하는데 유용하게 사용 할 수 있다.

STL set 특징

Unreal TSet 의 특징

사용 예시

                
                    TSet<int32> Int32Set;
                    for(int32 i=1 ; i <= 10;++i){
                        Int32Set.Add(i);
                    }

                    Int32Set; // {1,2,3,4,5,6,7,8,9,10}

                    int32Set.Remove(2); // TSet 에 RemoveAll 은 없다.
                    int32Set.Remove(4);
                    int32Set.Remove(6);
                    int32Set.Remove(8);
                    int32Set.Remove(10); // {1,invalid,3,invalid,5,invalid,7,invalid,9,invalid}
                    int32Set.Add(2);
                    int32Set.Add(4);
                    int32Set.Add(6);
                    int32Set.Add(8);
                    int32Set.Add(10); // {1,10,3,8,5,6,7,4,9,2}

                    // 채워질때는 거꾸로 채워졌다.

                
            

자료구조의 시간 복잡도 비교

  • 자료구조의 시간 복잡도 (TimeComplexity)
  • TArray TSet
    접근 O(1) O(1)
    검색 O(N) O(1)
    삽입 O(N) O(1)
    삭제 O(N) O(1)
    빈틈없는 메모리
    가장 높은 접근성는
    가장 높은 순회성능
    빈틈있는 메모리
    빠른 중복 감지