Deff_Dev

[Unity/C#] HashSet를 이용한 중복 제거 본문

Unity(유니티)/유니티 공부

[Unity/C#] HashSet를 이용한 중복 제거

Deff_a 2024. 6. 21. 23:39

 

HashSet

  • 중복 값을 허용하지 않는 컬렉션 

HashSet의 장점

  • 중복 제거 : 중복된 값이 자동으로 제거된다.
  • 빠른 검색 : 값이 존재하는지 탐색하는데 시간 복잡도가 O(1)이다.
  • 빠른 삽입/삭제 : 요소의 삽입/삭제의 평균 시간 복잡도는 O(1)이다.

HashSet의 단점

  1. 순서 없음: HashSet은 요소들의 순서를 보장하지 않는다.
  2. 메모리 사용: 내부적으로 해시 테이블을 사용하므로, 메모리 사용량이 상대적으로 클 수 있다.
  3. 가공 성능: 저장된 요소들을 특정 순서로 가공하거나 순회할 때는 성능이 떨어질 수 있다.

HashSet은 중복을 제거하고 빠른 검색이 필요한 경우에 적합한 자료구조이다.

저장된 요소들을 특정 순서대로 가공하거나 순회해야 하는 경우에는 다른 자료구조를 사용하는 것이 좋다.

 

 

HashSet<T> 클래스 (System.Collections.Generic)

값 집합을 나타냅니다.

learn.microsoft.com

 


문제 상황

오브젝트 풀링을 이용하여 몬스터를 소환하는 로직을 구현할 때, 스폰 이펙트를 추가한 뒤, 몬스터가 같은 스폰포인트에 소환하는 문제가 발생했다.


 해결 과정

현재 소환된 Spawn Effect들의 Transform 정보를 가지고있는 List를 선언하고 Find를 이용하여 중복된 Transform 값이 나오지 않도록 코드를 작성했다.

  [field:SerializeField]public List<Enemy> SpawnEnemyList { get; private set; }
  [field:SerializeField]public Transform[] EnemySpawnPoints { get; private set; }
  [field:SerializeField] public int CurrentEnemySpawnNum{ get; private set; }
  private List<Transform> spawnPointCompleteList = new List<Transform>(); // 중복 제거 리스트
   
 private IEnumerator SpawnEnemyCouroutine()
   {
      for (int i = 0; i < CurrentEnemySpawnNum; i++)
      {
         // 오브젝트 풀을 이용한 이펙트 생성
         SpawnEffect effect = GameManager.Instance.Pool.SpawnFromPool(EPoolObjectType.SpawnEffect).ReturnMyComponent<SpawnEffect>();
         effect.OnSpawnEnemy += ActivateEnemy;
         
         while (true) // 위치 설정
         {
            int rand = Random.Range(0, EnemySpawnPoints.Length);

            if (!spawnPointCompleteList.Find(obj => EnemySpawnPoints[rand].position == obj.transform.position))
            {
               effect.Init(EnemySpawnPoints[rand].position, spawnEffectLerpTime);
               spawnPointCompleteList.Add(EnemySpawnPoints[rand]);
               break;
            }
         }
         yield return wait;
      }
   }

 

문제는 해결됐지만 중복 제거를 하는 용도로 List를 사용하는 것이 비효율적이라고 생각이들어 다른 방법을 찾아봤고 HashSet을 이용하기로 했다.

 

  [field:SerializeField]public List<Enemy> SpawnEnemyList { get; private set; }
  [field:SerializeField]public Transform[] EnemySpawnPoints { get; private set; }
  [field:SerializeField] public int CurrentEnemySpawnNum{ get; private set; }
  private HashSet<Transform> spawnPointCompleteHashSet = new HashSet<Transform>(); // 중복 포지션 판별 해시셋
   
 private IEnumerator SpawnEnemyCouroutine()
   {
      for (int i = 0; i < CurrentEnemySpawnNum; i++)
      {
         // 오브젝트 풀을 이용한 이펙트 생성
         SpawnEffect effect = GameManager.Instance.Pool.SpawnFromPool(EPoolObjectType.SpawnEffect).ReturnMyComponent<SpawnEffect>();
         effect.OnSpawnEnemy += ActivateEnemy;
         
         while (true) // 위치 설정
         {
            int rand = Random.Range(0, EnemySpawnPoints.Length);

           if (spawnPointCompleteHashSet.Add(EnemySpawnPoints[rand])) // 해시 셋은 중복 추가가 안되기 때문에 중복 포지션이라면 false 반환
            {
               effect.Init(EnemySpawnPoints[rand].position, spawnEffectLerpTime);
               break;
            }
         }
         yield return wait;
      }
   }

 

HashSet을 이용하여 중복된 Transform 정보를 저장하지 않도록 코드를 리팩토링했다.

 

중복 제거를 구현할 때는 HashSet도 정말 좋은 방법인 것 같다.