C# Koleksiyonlar: List, Dictionary, Queue, Stack
C# koleksiyonlarını öğrenin: List, Dictionary, Queue ve Stack. Veri yönetimi, ekleme/çıkarma ve kullanım senaryoları örneklerle.
C#’ta koleksiyonlar, birden fazla veriyi dinamik olarak saklamak ve verimli biçimde işlemek için kullanılır.
Diziler sabit boyutludur; koleksiyonlar ise gerektiğinde büyüyüp küçülebilir. Bu makalede
List<T>, Dictionary<TKey,TValue> (ve KeyValuePair<,>),
SortedList<TKey,TValue>, Queue<T>, Stack<T>,
HashSet<T> ve LinkedList<T> yapıları örneklerle ele alınır.
List<T>
List<T>, aynı türden elemanları sıralı olarak tutan, dinamik olarak büyüyebilen bir listedir.
Ekleme/çıkarma, arama, sıralama gibi işlemler için zengin API sunar.
using System;
using System.Collections.Generic;
using System.Linq;
var sayilar = new List<int> { 5, 1, 9 };
sayilar.Add(3); // sona ekleme
sayilar.Insert(1, 7); // belirli konuma ekleme
sayilar.Remove(9); // değere göre silme (ilk eşleşen)
sayilar.RemoveAll(x => x % 2 == 1); // tek sayıları sil
sayilar.AddRange(new[] { 10, 2, 8 });
sayilar.Sort(); // doğal sıralama (artan)
sayilar.Reverse(); // ters çevir
Console.WriteLine(string.Join(", ", sayilar)); // 10, 8, 7, 5, 3, 2 → (tekler silinmeden önceki örnek için)
Console.WriteLine(sayilar.Contains(8)); // true
// LINQ ile filtreleme
var ciftler = sayilar.Where(n => n % 2 == 0).ToList();
Console.WriteLine(string.Join(", ", ciftler));
Dictionary<TKey,TValue> ve KeyValuePair<TKey,TValue>
Dictionary, anahtar–değer çiftleri saklar. Anahtarlar benzersizdir.
Yüksek performanslı erişim için hash tabanlıdır.
using System;
using System.Collections.Generic;
var stokFiyat = new Dictionary<string, decimal>(StringComparer.OrdinalIgnoreCase)
{
["Elma"] = 5.75m,
["Armut"] = 7.25m
};
// Ekleme ve güncelleme
stokFiyat["Muz"] = 12m; // yoksa ekler, varsa günceller
// Güvenli okuma
if (stokFiyat.TryGetValue("ELMA", out var fiyat))
Console.WriteLine($"Elma: {fiyat} TL");
// Anahtar var mı?
if (!stokFiyat.ContainsKey("Kiraz"))
stokFiyat.Add("Kiraz", 18.5m);
// KeyValuePair ile gezinme
foreach (KeyValuePair<string, decimal> kv in stokFiyat)
Console.WriteLine($"{kv.Key} → {kv.Value} TL");
// Değer güncelleme deseni
stokFiyat["Elma"] = stokFiyat["Elma"] * 1.10m; // %10 zam
// Kendi equality comparer'ınızla (ör: case-insensitive) sözlük oluşturma zaten yukarıda gösterildi.
İpucu: KeyValuePair<TKey,TValue>, sözlük üzerinde gezinirken her girdiyi
.Key ve .Value ile okumanızı sağlar. Değer güncelleme için doğrudan dict[key] kullanılabilir.
SortedList<TKey,TValue>
SortedList, anahtarları artan sırada tutar ve Keys/Values dizileri üzerinden
indeksli erişim sağlar. Hem Dictionary hem de dizi benzeri indeksleme özelliklerini birleştirir.
using System;
using System.Collections.Generic;
var fiyatListesi = new SortedList<string, decimal>(StringComparer.OrdinalIgnoreCase)
{
["Armut"] = 7.25m,
["Elma"] = 5.75m,
["Muz"] = 12m,
["Kiraz"] = 18.5m
};
// Sıralı anahtarlar:
Console.WriteLine(string.Join(", ", fiyatListesi.Keys)); // Armut, Elma, Kiraz, Muz
// İndeks ile erişim:
for (int i = 0; i < fiyatListesi.Count; i++)
Console.WriteLine($"#{i} {fiyatListesi.Keys[i]} → {fiyatListesi.Values[i]}");
// Belirli bir aralıkta gezmek (ör. ilk 2 öğe):
for (int i = 0; i < Math.Min(2, fiyatListesi.Count); i++)
Console.WriteLine(fiyatListesi.Keys[i]);
// Özel sıralama (ters alfabetik) için custom comparer:
var ters = new SortedList<string, decimal>(Comparer<string>.Create((a,b) => StringComparer.OrdinalIgnoreCase.Compare(b,a)));
ters["Elma"] = 5.75m;
ters["Armut"] = 7.25m;
Console.WriteLine(string.Join(", ", ters.Keys)); // Ters sırada
SortedListekleme/silmede, sıralamayı korumak için öğeleri kaydırabilir (indeks tabanlı). Çok sık ekleme/çıkarma varsaSortedDictionarydüşünün.- İndeks üzerinden aralık işlemleri gerekiyorsa
SortedListavantajlıdır.
Queue<T> (FIFO)
Queue, ilk giren ilk çıkar mantığıyla çalışır; iş akışları ve görev sıraları için uygundur.
using System;
using System.Collections.Generic;
var kuyruk = new Queue<string>();
kuyruk.Enqueue("Sipariş#1001");
kuyruk.Enqueue("Sipariş#1002");
kuyruk.Enqueue("Sipariş#1003");
Console.WriteLine(kuyruk.Peek()); // sıradaki (çıkarmaz): Sipariş#1001
Console.WriteLine(kuyruk.Dequeue()); // çıkarır: Sipariş#1001
Console.WriteLine(kuyruk.Count); // 2
Stack<T> (LIFO)
Stack, son giren ilk çıkar mantığıyla çalışır; geri alma (undo), gezinti geçmişi gibi senaryolarda kullanılır.
using System;
using System.Collections.Generic;
var yigin = new Stack<string>();
yigin.Push("Adım-1");
yigin.Push("Adım-2");
yigin.Push("Adım-3");
Console.WriteLine(yigin.Peek()); // Adım-3
Console.WriteLine(yigin.Pop()); // Adım-3 (çıkarıldı)
Console.WriteLine(yigin.Pop()); // Adım-2
HashSet<T>
HashSet, benzersiz eleman kümesini tutar. Tekrarlı eklemeler görmezden gelinir.
Küme işlemleri (birleşim, kesişim, fark) için idealdir.
using System;
using System.Collections.Generic;
var a = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "elma", "armut", "muz" };
var b = new HashSet<string> { "Muz", "kiraz" };
var birlik = new HashSet<string>(a);
birlik.UnionWith(b); // A ∪ B
var kesisim = new HashSet<string>(a);
kesisim.IntersectWith(b); // A ∩ B
var fark = new HashSet<string>(a);
fark.ExceptWith(b); // A \ B
Console.WriteLine(string.Join(", ", birlik)); // elma, armut, muz, kiraz
Console.WriteLine(string.Join(", ", kesisim)); // muz
Console.WriteLine(string.Join(", ", fark)); // elma, armut
Console.WriteLine(a.Add("Elma")); // false (zaten var - case-insensitive)
İpucu: Üyelik testleri (Contains) ve ekleme HashSet’te genellikle O(1) amortized maliyetlidir; büyük veri kümelerinde tekrarlı elemanları hızlı ayıklamak için idealdir.
LinkedList<T>
LinkedList, çift yönlü bağlı liste yapısıdır. Düğümler (nodes) üzerinden ekleme/çıkarma O(1) maliyetle yapılabilir.
İndeksli rastgele erişim yoktur; belirli bir konumu bulmak için gezinmek gerekir.
using System;
using System.Collections.Generic;
var liste = new LinkedList<string>();
var node1 = liste.AddFirst("Başlangıç"); // "Başlangıç"
var node2 = liste.AddLast("Son"); // "Başlangıç" <-> "Son"
liste.AddAfter(node1, "Ara-1"); // "Başlangıç" <-> "Ara-1" <-> "Son"
liste.AddBefore(node2, "Ara-2"); // "Başlangıç" <-> "Ara-1" <-> "Ara-2" <-> "Son"
// Düğüm silme
var ara1 = node1.Next; // "Ara-1"
liste.Remove(ara1);
// Gezinme
for (var n = liste.First; n != null; n = n.Next)
Console.WriteLine(n.Value); // Başlangıç, Ara-2, Son
- Baş/son eklemeleri ve bilinen düğümün önüne/arkasına ekleme/silme hızlıdır.
- Rastgele erişim (örn. 1000. elemanı getir) için uygun değildir;
List<T>tercih edilir.
Örnek Uygulama: Sipariş İşleme Hattı
Aşağıdaki senaryoda farklı koleksiyonlar birlikte kullanılır: Queue ile bekleyen siparişler, Dictionary ile stok ve fiyatlar, HashSet ile benzersiz müşteriler, Stack ile geri alma (undo), SortedList ile fiyat listesi ve LinkedList ile “son bakılan ürünler” tutulur.
using System;
using System.Collections.Generic;
var bekleyen = new Queue<string>(new[] { "SIP-1001", "SIP-1002" });
var stok = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
["Elma"] = 100, ["Armut"] = 50, ["Muz"] = 30
};
var fiyat = new SortedList<string, decimal> { ["Armut"] = 7.25m, ["Elma"] = 5.75m, ["Muz"] = 12m };
var musteriSet = new HashSet<string>(); // benzersiz müşteri kimlikleri
var undo = new Stack<Action>(); // geri alma yığını
var sonBakilan = new LinkedList<string>(); // en son bakılan ürünler
void SepeteEkle(string urun, int adet)
{
if (stok.TryGetValue(urun, out var s) && s >= adet)
{
stok[urun] = s - adet;
undo.Push(() => stok[urun] += adet); // geri alma işlemi
sonBakilan.AddFirst(urun);
Console.WriteLine($"{adet} x {urun} eklendi. Kalan: {stok[urun]}");
}
else
{
Console.WriteLine($"Yetersiz stok: {urun}");
}
}
void SiparisiIsle(string sipNo, string musteriId)
{
Console.WriteLine($"İşleniyor: {sipNo}");
musteriSet.Add(musteriId);
SepeteEkle("Elma", 3);
SepeteEkle("Armut", 2);
}
while (bekleyen.Count > 0)
{
var sip = bekleyen.Dequeue();
SiparisiIsle(sip, "MUST-001");
}
Console.WriteLine($"Benzersiz müşteri sayısı: {musteriSet.Count}");
Console.WriteLine($"Fiyat listesi (sıralı): {string.Join(", ", fiyat.Keys)}");
// Hatalı işlem olduysa geri al:
if (undo.Count > 0)
{
var geri = undo.Pop();
geri(); // son sepete ekleme geri alındı
}
Console.WriteLine("Son bakılan 3 ürün:");
int k = 0;
for (var n = sonBakilan.First; n != null && k < 3; n = n.Next, k++)
Console.WriteLine($"- {n.Value}");
TL;DR
- List<T>: Dinamik, indeks tabanlı sıralı liste. Sık rastgele erişim için ideal.
- Dictionary<TKey,TValue>: Anahtar–değer; hızlı arama. KeyValuePair ile gezin.
- SortedList<TKey,TValue>: Anahtarlar sıralı + indeksli erişim. Sık aralık/indeks erişimi için iyi.
- Queue<T> (FIFO): İş akışı/kuyruk işlemleri.
- Stack<T> (LIFO): Geri alma (undo), geçmiş.
- HashSet<T>: Benzersiz eleman kümesi; birleşim/kesişim/fark operasyonları.
- LinkedList<T>: Düğüm tabanlı liste; baş/son ve bilinen düğüme göre hızlı ekleme/çıkarma.
Seçim rehberi: Rastgele erişim → List, hızlı anahtar araması → Dictionary, sıralı anahtar ve indeks → SortedList, benzersiz küme → HashSet, FIFO → Queue, LIFO → Stack, hızlı düğüm ekle/çıkar → LinkedList.
İlişkili Makaleler
C# Diziler (Array)
C#’ta dizileri (array) öğrenin. Eleman ekleme, erişim, döngülerle gezinme ve temel array işlemleri örneklerle anlatılıyor.
C# Döngüler (for, foreach, while, do-while)
C#’ta for, foreach, while ve do-while döngülerinin nasıl kullanıldığını öğrenin. Tekrar eden işlemleri yönetmek için pratik örnekler keşfedin.
C# Generic Yapıları (List<T>, Dictionary<TKey,TValue>)
C# generic yapılarını öğrenin. List<T> ve Dictionary<TKey,TValue> ile tip güvenliği, yeniden kullanılabilirlik ve pratik örnekler.
C# Lambda İfadeleri
C#’ta lambda ifadelerini öğrenin. Kısa sözdizimi, Func ve Action kullanımı ile LINQ sorgularında pratik örnekler keşfedin.