게임 개발을 하다 보면 리스트에서 원소를 지우면서, 동시에 리스트에서 방금 지워진 원소에 대해 어떤 조작을 해야 하는 패턴을 굉장히 흔하게 접하게 된다. 흔한 예를 들면 유닛이 사망하면 유닛을 리스트에서 지우면서 사망한 유닛의 사망 처리를 하는 패턴을 예로 들 수 있다.
이런 경우 List.RemoveAll 을 어떻게든 쓰거나 루프를 여러번 도는 식으로 코딩하는 경우를 흔하게 볼 수 있다.
이런 경우에 가장 효율적인 코드는 이미 잘 알려져 있다.
for (var i = list.Count - 1; i >= 0; i--)
{
if (list[i].Removed)
{
list[i].OnRemove();
list[i] = list[^1];
list.RemoveAt(list.Count - 1);
}
}
지워야 하는 원소가 있으면 필요한 처리 (여기서는 OnRemove호출)를 하고 마지막 원소로 덮어씌운 후 마지막 원소를 지운다. List.RemoveAt은 마지막 원소를 지울 경우 아무 것도 하지 않고 내부의 size만 감소시키기 때문에 부하가 사실상 0과 다름없다.
덧붙여..
- 역방향으로 순회하는 이유는 정방향으로 순회하게 되면 마지막 원소와 교체했을 때 해당 인덱스를 다시 검사해야 하기 때문이다.
- 이 방법은 리스트에 담긴 순서가 변경되므로 순서가 중요한 상황이면 사용할 수 없다.
- 리스트의 순서를 보존하려면 마지막 원소와 교체하지 않고 그냥 RemoveAt을 하면 되지만 뒤쪽의 원소들을 앞으로 당기느라 배열 복사가 일어난다.
- 이런 단순 리스트에서 삭제하는 코드가 일반적으로 성능에 영향을 미치지는 않지만, 만약 List에서 순서를 유지하며 원소들을 삭제하는 부분이 정말로 부하가 있다면 그 경우에는 List 대신 다른 컬렉션을 사용해야 한다.
'Unity > 코드' 카테고리의 다른 글
C#에서 데이터를 압축하는 코드 (0) | 2024.02.29 |
---|---|
코루틴 여러개가 모두 완료될때까지 대기하는 코드 (0) | 2024.02.20 |