Çoğu C# geliştiricisi her şey için List<T> kullanır. Küçük veri setlerinde bu sorun yaratmaz; ama veri büyüdükçe yanlış koleksiyon seçimi, saniyelerin dakikalara dönüşmesine yol açabilir. Hangi koleksiyonun hangi işte iyi olduğunu bilmek, en ucuz performans iyileştirmelerinden biridir.
Hızlı karşılaştırma
| Koleksiyon | Güçlü olduğu iş | Arama |
|---|---|---|
T[] (dizi) | Sabit boyutlu, yüksek performanslı erişim | İndeksle çok hızlı |
List<T> | Sıralı, büyüyebilen liste | İndeksle hızlı, değerle yavaş |
Dictionary<TKey, TValue> | Anahtarla arama | Çok hızlı |
HashSet<T> | Benzersiz öğeler, "var mı?" sorusu | Çok hızlı |
Queue<T> | İlk giren ilk çıkar (iş kuyruğu) | — |
Stack<T> | Son giren ilk çıkar (geri al işlemleri) | — |
SortedDictionary | Anahtara göre sıralı tutma | Hızlı |
Klasik hata: Listede arama
// 50.000 sipariş için müşteri arama: her seferinde tüm listeyi tarar
foreach (var s in siparisler)
{
var musteri = musteriler.First(m => m.Id == s.MusteriId);
}
// Önce sözlüğe çevir: her arama neredeyse anında
var musteriSozlugu = musteriler.ToDictionary(m => m.Id);
foreach (var s in siparisler)
{
var musteri = musteriSozlugu[s.MusteriId];
}
İlk sürümde işlem sayısı iki listenin boyutlarının çarpımı kadar büyür; ikincisinde ise yaklaşık toplamı kadar. Büyük veride fark dramatiktir. Aynı mantık veritabanı tarafında N+1 problemi olarak karşımıza çıkar.
HashSet ile "var mı?" kontrolü
var engelliEpostalar = new HashSet<string>(liste, StringComparer.OrdinalIgnoreCase);
if (engelliEpostalar.Contains(eposta)) { /* ... */ }
Karşılaştırıcı belirtmek, büyük-küçük harf ve Türkçe "İ" sorunlarını baştan önler.
Salt okunur ve Frozen koleksiyonlar
IReadOnlyList<T>,IReadOnlyDictionary: Metot dönüşlerinde, çağıranın koleksiyonu değiştirmemesi gerektiğini anlatır.FrozenDictionaryveFrozenSet: Uygulama başlangıcında bir kez oluşturulan ve sonra sadece okunan veriler (ülke kodları, ayarlar, izin listeleri) için okuma hızında optimize edilmiştir.- Immutable koleksiyonlar: Her değişiklikte yeni bir kopya üretir; çok iş parçacıklı senaryolarda güvenlidir.
Eşzamanlı (concurrent) koleksiyonlar
Birden fazla iş parçacığı aynı koleksiyona yazıyorsa normal Dictionary güvenli değildir. ConcurrentDictionary, ConcurrentQueue veya kanal (Channel<T>) yapıları kullanılmalıdır. Arka plan işlerinde bu durum sık görülür. Bkz. BackgroundService ve async/await.
Başlangıç kapasitesi
Eklenecek öğe sayısı biliniyorsa kapasiteyi baştan vermek, tekrar tekrar büyüme maliyetini önler:
var liste = new List<SatirDto>(kayitSayisi);
Sık sorulan sorular
Dönüş tipi olarak List mi, IEnumerable mi kullanmalıyım?
Çağıran yalnızca dolaşacaksa IEnumerable<T> veya IReadOnlyList<T>; sayı ve indeks erişimi gerekiyorsa IReadOnlyList<T> iyi bir dengedir. Ertelenmiş yürütme davranışı için bkz. LINQ rehberi.
Küçük listelerde de dikkat etmeli miyim?
Birkaç düzine öğede fark hissedilmez. Okunabilirliği öne alın; büyüyebilecek verilerde ise doğru yapıyı baştan seçin.
Sonuç
Doğru koleksiyon, kodu değiştirmeden büyük performans kazancı sağlar. Anahtarla aramada Dictionary, benzersizlikte HashSet, sabit verilerde Frozen koleksiyonlar; çoğu senaryonun cevabıdır.