1. 컬렉션에서 기본적인 검색 기능
보통 “검색”이랑 “필터링”을 비슷하게 생각하는데, 프로그래밍에서는 좀 달라.
- 필터링 — 컬렉션에서 어떤 조건에 맞는 모든 요소를 뽑아내는 거야 (예: 10보다 큰 모든 숫자).
- 검색 — 보통 한 개의 요소만 찾거나, 특정 값이 있는지 확인하거나, 첫 번째로 맞는 것만 찾는 거지.
도서관에 비유하면: 필터링은 “우주” 주제의 모든 책을 모으는 거고, 검색은 “‘율리시스’라는 책 있어요?” 또는 “파란색 표지의 첫 번째 책이 어디 있어요?”라고 묻는 거랑 비슷해.
.NET의 모든 주요 컬렉션(배열, 리스트, 집합, 딕셔너리 등)은 요소를 찾거나 존재 여부를 확인하는 여러 방법을 지원해.
가장 많이 쓰는 메서드들을 살펴보자:
| 컬렉션 | 존재 확인 | 인덱스 찾기 | 요소 찾기 | 키로 찾기 |
|---|---|---|---|---|
|
|
|
|
- |
(배열) |
/ |
|
- | - |
|
|
- | - | - |
|
, |
- | - | 인덱서 있음 |
어떤 메서드는 특정 타입에서만 쓸 수 있어.
2. 리스트에서 찾기: List<T>
List<T>는 임의 접근이 가능한 가장 범용적인 컬렉션 중 하나야. 주요 검색 메서드는 이래:
요소 존재 확인: Contains
이 메서드는 리스트에 원하는 요소가 있는지 알려줘:
List<string> fruits = new List<string> { "사과", "바나나", "키위" };
bool hasKiwi = fruits.Contains("키위"); // true
bool hasMango = fruits.Contains("망고"); // false
Console.WriteLine(hasKiwi); // True
Contains는 내부적으로 Equals로 컬렉션을 순회해.
인덱스 찾기: IndexOf
요소가 리스트에서 어디에 있는지(몇 번째인지) 알고 싶으면:
int index = fruits.IndexOf("바나나"); // 1 (0부터 시작)
int absentIndex = fruits.IndexOf("수박"); // -1 (없으면)
Console.WriteLine(index);
Console.WriteLine(absentIndex);
첫 번째로 맞는 것 찾기: Find
정확한 값이 아니라 조건으로 찾고 싶으면 Find (혹은 인덱스가 필요하면 FindIndex)를 써:
// 이름이 4글자보다 긴 첫 번째 과일 찾기
string longFruit = fruits.Find(fruit => fruit.Length > 4); // "사과"
Console.WriteLine(longFruit);
참고: 못 찾으면 타입의 기본값을 반환해(참조형이면 null).
여러 개 찾기(필터링): FindAll
필터링(조건에 맞는 모든 것)이 필요하면 FindAll을 써:
// 이름에 '이'가 들어간 모든 과일
List<string> withI = fruits.FindAll(f => f.Contains('이'));
foreach (var fruit in withI)
Console.WriteLine(fruit); // "키위"
3. 배열에서 찾기: Array 클래스 메서드
배열은 Find 같은 메서드가 없지만, List<T>처럼 쓸 수 있는 정적 클래스 Array가 있어:
int[] numbers = { 1, 2, 3, 2, 4 };
int pos = Array.IndexOf(numbers, 2); // 1 — 첫 번째 2의 인덱스
int lastPos = Array.LastIndexOf(numbers, 2); // 3 — 마지막 2의 인덱스
Console.WriteLine(pos + ", " + lastPos);
조건으로 찾으려면 그냥 for문 쓰면 돼:
int firstGreaterThanTwo = -1;
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] > 2)
{
firstGreaterThanTwo = numbers[i];
break;
}
}
Console.WriteLine(firstGreaterThanTwo); // 3
4. 모든 컬렉션에서 쓸 수 있는 방법
많은 컬렉션이 Contains, IndexOf, Find 같은 메서드를 제공해. 더 복잡한 게 필요하면 직접 for문으로 순회하면 돼.
예시: "ㅂ"으로 시작하는 과일이 있는지 확인
bool hasB = false;
foreach (var f in fruits)
{
if (f.StartsWith("바"))
{
hasB = true;
break;
}
}
Console.WriteLine(hasB); // True
예시: "이"가 들어간 첫 번째 과일 찾기
string withI = null;
foreach (var f in fruits)
{
if (f.Contains('이'))
{
withI = f;
break;
}
}
Console.WriteLine(withI); // "키위"
예시: 사용자 객체에서 찾기
class Student
{
public string Name;
public int Group;
public int Id;
}
List<Student> students = new List<Student>
{
new Student { Name = "이반", Group = 101, Id = 1 },
new Student { Name = "마리야", Group = 101, Id = 2 },
new Student { Name = "표트르", Group = 102, Id = 3 },
};
// Id == 2인 학생 찾기
Student maria = null;
foreach (var s in students)
{
if (s.Id == 2)
{
maria = s;
break;
}
}
if (maria != null)
Console.WriteLine(maria.Name); // "마리야"
else
Console.WriteLine("학생을 찾을 수 없음");
5. HashSet<T>에서 찾기: “있냐 없냐”만!
집합(HashSet<T>)은 “이거 있냐?”를 엄청 빠르게 확인하려고 만든 거야. 인덱스로 찾는 건 안 되지만, 존재 여부는 진짜 빨라:
HashSet<int> set = new HashSet<int> { 1, 3, 5, 7 };
bool hasThree = set.Contains(3); // True
Console.WriteLine(hasThree);
// 조건으로 찾으려면(예: 짝수 있는지):
bool hasEven = false;
foreach (var x in set)
{
if (x % 2 == 0)
{
hasEven = true;
break;
}
}
Console.WriteLine(hasEven); // False
6. 딕셔너리에서 찾기: Dictionary<TKey, TValue>
딕셔너리는 “키-값” 쌍의 컬렉션이야. 키로 찾는 게 진짜 강점이지.
키 존재 확인
Dictionary<int, string> idToName = new Dictionary<int, string>
{
{ 1, "바샤" }, { 2, "카탸" }
};
if (idToName.ContainsKey(2))
Console.WriteLine(idToName[2]); // "카탸"
키로 값 찾기: 안전하게!
if (idToName.TryGetValue(3, out string result))
Console.WriteLine(result);
else
Console.WriteLine("그런 Id의 학생 없음"); // 이게 출력됨
값으로 찾기(잘 안 쓰고 느림):
bool containsVasya = idToName.ContainsValue("바샤");
Console.WriteLine(containsVasya); // True
값이나 키 조건으로 찾기
// 이름이 "카"로 시작하는 첫 번째 Id 찾기
KeyValuePair<int, string> entry = default;
bool found = false;
foreach (var pair in idToName)
{
if (pair.Value.StartsWith("카"))
{
entry = pair;
found = true;
break;
}
}
if (found)
Console.WriteLine($"{entry.Key}: {entry.Value}"); // "2: 카탸"
7. 유용한 팁
검색 메서드 표
| 컬렉션 타입 | 존재 확인 Contains | 인덱스 찾기 IndexOf | 조건으로 찾기 Find | 키로 안전하게 찾기 TryGetValue |
|---|---|---|---|---|
|
네 | 네 | 네 () |
- |
(배열) |
네 (/for문) |
네 () |
for문 | - |
|
네 | - | 아니(직접 해야 함) | - |
|
네 () |
- | 직접 키/값으로 | 네 |
직접 for문으로 찾기
완전히 이해하려면 직접 컬렉션에서 찾는 메서드를 만들어보자. 예를 들어, 지정한 값보다 큰 첫 번째 요소의 인덱스를 찾는 함수:
static int FindFirstGreaterIndex(IEnumerable<int> collection, int minValue)
{
int index = 0;
foreach (var item in collection)
{
if (item > minValue)
return index;
index++;
}
return -1; // 못 찾음
}
var nums = new List<int> { 1, 4, 7, 2 };
Console.WriteLine(FindFirstGreaterIndex(nums, 3)); // 1 (숫자 4)
여기서는 (List<T>, int[], HashSet<T> 등) 타입이나 내부 구현에 상관없이 쓸 수 있어.
실제 문제에서 검색 활용 예시
- DB에서 로그인이나 이메일로 사용자 찾기.
- 장바구니에 상품이 있는지 확인.
- 설정값을 이름으로 빠르게 찾기.
- 특정 단계가 이미 실행됐는지 확인(workflow 등).
- 로그 배열에서 에러 위치 찾기.
미들급 개발자 면접에서는 꼭 이런 질문 나와: “컬렉션에서 요소를 어떻게 찾나요? 조건에 맞는 첫 번째/모든/인덱스 반환하는 메서드를 어떻게 만들 거예요?” 연습 많이 해두면 좋아!
8. 검색할 때 흔히 하는 실수와 특징
중요한 점: 검색 메서드는 객체 비교 방식에 따라 달라져. 예를 들어, 리스트에 직접 만든 클래스를 넣으면 기본 비교는 “참조” 비교야! “내용”으로 검색하려면 Equals랑 GetHashCode를 오버라이드해야 해(이건 다음 강의에서 더 자세히 다룰게).
문제 예시:
var s1 = new Student { Name = "예고르", Id = 42 };
students.Add(s1);
// 이제 같은 Id와 이름으로 새 객체 생성:
var s2 = new Student { Name = "예고르", Id = 42 };
Console.WriteLine(students.Contains(s2)); // False (!)
컴파일러는 필드가 아니라 참조로 비교해서(다른 객체라서 그래).
GO TO FULL VERSION