การตรวจสอบว่า Array/List ของ String มีค่าที่ต้องการหรือไม่ มีหลายวิธี แตกต่างใน Performance และ Features
ตัวอย่างข้อมูล
string[] fruits = { "Apple", "Banana", "Cherry", "Date", "Elderberry" };
วิธีที่ 1: Array.Contains / List.Contains (Simple, Case-Sensitive)
bool hasBanana = fruits.Contains("Banana"); // true
bool hasMango = fruits.Contains("Mango"); // false
bool hasBANANA = fruits.Contains("BANANA"); // false (Case-Sensitive!)
- O(N) Linear Search
- ใช้ Equality Comparer Default (Ordinal Case-Sensitive)
วิธีที่ 2: LINQ Any() + StringComparison (Case-Insensitive)
bool hasBananaCI = fruits.Any(f => f.Equals("banana", StringComparison.OrdinalIgnoreCase)); // true
bool hasMangoCI = fruits.Any(f => f.Equals("mango", StringComparison.OrdinalIgnoreCase)); // false
- Short-circuit (หยุดเจอตัวแรก)
- รองรับ StringComparison ทุกแบบ
วิธีที่ 3: HashSet<T> (O(1) Lookup - สำหรับหลายครั้ง)
// สร้าง HashSet ครั้งเดียว (Pre-compute)
var fruitSet = new HashSet<string>(fruits, StringComparer.OrdinalIgnoreCase);
// Lookup หลายครั้ง - เร็วมาก
bool hasBanana = fruitSet.Contains("banana"); // true
bool hasBANANA = fruitSet.Contains("BANANA"); // true
bool hasMango = fruitSet.Contains("mango"); // false
วิธีที่ 4: Array.IndexOf / Array.FindIndex
int index = Array.IndexOf(fruits, "Cherry"); // 2 (Index)
int indexCI = Array.FindIndex(fruits, f => f.Equals("cherry", StringComparison.OrdinalIgnoreCase)); // 2
bool found = index >= 0;
วิธีที่ 5: LINQ FirstOrDefault / SingleOrDefault
// หา Object เต็ม (ไม่ใช่แค่ bool)
string found = fruits.FirstOrDefault(f => f.Equals("banana", StringComparison.OrdinalIgnoreCase));
// คืน "Banana" หรือ null
string exact = fruits.SingleOrDefault(f => f == "Cherry"); // คืน "Cherry" (ต้องมี 1 ตัวพอดี)
Benchmark (10,000 Items, 1000 Lookups)
| Method |
Time (ms) |
Note |
| Array.Contains (Case-Sensitive) |
~15 |
Baseline |
| LINQ Any (OrdinalIgnoreCase) |
~45 |
Delegate Overhead |
| HashSet.Contains (OrdinalIgnoreCase) |
~0.5 |
Fastest for Repeated Lookups |
| Array.IndexOf |
~12 |
Slightly Faster than Contains |
Best Practice แบบสรุป
| Scenario |
Recommended |
| Check ครั้งเดียว, Case-Sensitive |
array.Contains(value) |
| Check ครั้งเดียว, Case-Insensitive |
array.Any(v => v.Equals(val, StringComparison.OrdinalIgnoreCase)) |
| Check หลายครั้ง (Repeated Lookups) |
สร้าง HashSet<string>(array, StringComparer.OrdinalIgnoreCase) แล้ว .Contains() |
| ต้องการ Index/Object |
Array.FindIndex / FirstOrDefault |
| Sorted Array |
Array.BinarySearch (O(log N)) |
ตัวอย่างครบถ้วน
static void Main()
{
string[] fruits = { "Apple", "Banana", "Cherry" };
// 1. Simple
Console.WriteLine($"Contains 'Banana': {fruits.Contains("Banana")}"); // True
// 2. Case-Insensitive Once
Console.WriteLine($"Any 'banana' (CI): {fruits.Any(f => f.Equals("banana", StringComparison.OrdinalIgnoreCase))}"); // True
// 3. Repeated Lookups -> HashSet
var fruitSet = new HashSet(fruits, StringComparer.OrdinalIgnoreCase);
Console.WriteLine($"HashSet 'BANANA': {fruitSet.Contains("BANANA")}"); // True
Console.WriteLine($"HashSet 'Mango': {fruitSet.Contains("Mango")}"); // False
// 4. Get Item
string found = fruits.FirstOrDefault(f => f.StartsWith("B", StringComparison.OrdinalIgnoreCase));
Console.WriteLine($"StartsWith B: {found}"); // "Banana"
}