1. List.Find란?

public T Find(Predicate<T> match);
  • List<T> 클래스에 포함된 메서드
  • 제네릭 리스트 안에서 특정 조건(Predicate)에 맞는 첫 번째 요소를 찾아서 반환
  • 조건에 맞는 요소가 없으면 default(T)를 반환 (참조형이면 null, 값형이면 0 등)

사용 예제

List<int> numbers = new List<int> { 1, 3, 5, 7, 9 };

// 조건: 짝수인 첫 번째 원소 찾기
int found = numbers.Find(n => n % 2 == 0);

Console.WriteLine(found); // 출력: 0 (int의 기본값)

과정:

  1. 리스트의 첫 번째 요소(1) → 조건 함수 실행 (1 % 2 == 0) → false
  2. 두 번째 요소(3) 검사 → false
  3. 끝까지 true가 없었으므로 default(int)인 0 반환

풀어서 쓴 형태

public static T MyFind<T>(List<T> list, Predicate<T> match)
{
    // 리스트 전체를 앞에서부터 순서대로 검사
    for (int i = 0; i < list.Count; i++)
    {
        // 조건(match)이 true라면 해당 요소 반환
        if (match(list[i]))
        {
            return list[i];
        }
    }

    // 조건을 만족하는 요소가 하나도 없으면 기본값 반환
    return default(T);
}

2. Find vs FindAll vs ForEach

조건을 만족하는 요소를 찾거나 수정할 때, Find 말고도 FindAll, ForEach를 활용할 수 있습니다.

 

(1) Find – 첫 번째 요소만

var found = achievements.Find(a => a.currentCount == a.maxCount);

if (found != null)
{
    found.isUnlocked = true;
}
  • 조건을 만족하는 첫 번째 요소만 반환
  • 여러 개 중 하나만 처리할 때 적합

(2) FindAll – 조건 맞는 모든 요소

var foundAll = achievements.FindAll(a => a.currentCount == a.maxCount);

foreach (var achievement in foundAll)
{
    achievement.isUnlocked = true;
}
  • 조건을 만족하는 모든 요소 반환
  • 반환된 리스트를 돌면서 원하는 수정 가능합니다.

(3) ForEach – 리스트 전체 순회

achievements.ForEach(a =>
{
    if (a.currentCount == a.maxCount)
    {
        a.isUnlocked = true;
    }
});
  • 리스트 전체를 순회하면서 조건 검사 및 수정
  • 가장 간결하지만, 가독성은 foreach보다 떨어질 수 있습니다.

(4) FindAll + ForEach 조합

achievements.FindAll(a => a.currentCount == a.maxCount)
            .ForEach(a =>
            {
                a.isUnlocked = true;
            });
// 조건에 맞는 요소만 필터링 (foreach의 if와 같은 역할)
.FindAll(a => a.currentCount == a.maxCount)

// 조건 맞는 요소에 대해 실행할 동작 (if문 내부 코드와 같은 역할)
    .ForEach(a => a.isUnlocked = true);

 

풀어서 쓰면:

foreach (var achievement in achievements)
        {
            if (achievement.currentCount == achievement.maxCount)
            {
                achievement.isUnlocked = true;
            }
        }

즉, foreach에서는 조건(if) + 실행(동작)을 한 블록 안에 같이 쓰는 반면,
FindAll + ForEach 조합에서는 조건(Filter)과 실행(Action)을 메서드 체인으로 분리해서 표현합니다.


그러면 의문이 들 수 있습니다.

foreach만 있으면 다 되는데, 왜 굳이 Find, FindAll, ForEach 같은 메서드를 따로 만들었을까?

이유 1. 간결성과 표현력

foreach는 가장 직관적이지만, 코드를 조금 길게 써야 합니다.
Find 같은 메서드를 쓰면 한 줄로 표현할 수 있습니다.

// foreach
string result = null;
foreach (var name in names)
{
    if (name.StartsWith("C"))
    {
        result = name;
        break;
    }
}

// Find
string result = names.Find(n => n.StartsWith("C"));

같은 기능을 더 짧고 의도 중심으로 표현 가능

이유 2. 재사용성과 조합성

Find, FindAll은 조건을 만족하는 요소 집합을 반환하므로, 다른 메서드와 쉽게 조합할 수 있습니다.

// 조건 맞는 모든 요소만 골라낸 뒤
var completed = achievements.FindAll(a => a.currentCount == a.maxCount);

// 다시 다른 작업에 활용 가능
completed.ForEach(a => a.isUnlocked = true);

이유 3. 함수형 스타일 & 가독성 (상황에 따라)

LINQ, 람다와 함께 쓰면 데이터 → 조건 → 결과 흐름을 선언적(Declarative) 으로 쓸 수 있습니다.

achievements
    .FindAll(a => a.currentCount == a.maxCount)
    .ForEach(a => a.isUnlocked = true);

업적 중에 완료된 것만 골라 언락한다라는 의도가 코드에서 바로 보입니다.

+ Recent posts