Merhaba Webkolog takipçileri!
Bugün yazılım geliştirmenin temel taşlarından biri olan koleksiyonları mercek altına alacağız. C# .NET Framework 4 ve Microsoft Visual Studio 2010 ortamında, verileri etkili bir şekilde depolamak, yönetmek ve işlemek için kullandığımız koleksiyonların ne olduğunu, ne işe yaradığını ve hangi durumlarda hangisini tercih etmemiz gerektiğini detaylıca inceleyeceğiz. Uygulamalarınızda daha temiz, daha verimli ve daha yönetilebilir kod yazmak istiyorsanız, doğru yerdesiniz!
Koleksiyonlar Nedir ve Neden İhtiyaç Duyarız?
Programlamada sıkça karşılaştığımız bir durum, aynı türden veya farklı türden birden fazla veri parçasını bir arada tutma ihtiyacıdır. İşte bu noktada koleksiyonlar devreye girer. Koleksiyonlar, verileri bir araya getirmek, düzenlemek ve üzerinde çeşitli işlemler yapmak için tasarlanmış özel sınıflardır. Dizilere benzemekle birlikte, dizilerin aksine çok daha esnek ve dinamik yapılar sunarlar.
Peki, dizilerle koleksiyonlar arasındaki farklar neler? Hazırladığım bir tabloyla bu farkları özetleyelim:
Özellik |
Diziler (System.Array) |
Koleksiyonlar (System.Collections, System.Collections.Generic) |
Temel Tür |
System.Array sınıfından türemiştir. |
System.Collections veya System.Collections.Generic namespace'lerinden gelir. |
Veri Türü |
Genellikle aynı türdeki verileri saklar. |
Hem aynı hem de farklı türdeki verileri saklayabilir (Non-Generic). Generic koleksiyonlar ise tip güvenlidir. |
Tip Dönüşümü |
Elemanlar üzerinde dönüşüm (casting) işlemine tabi olmaz. |
Non-Generic koleksiyonlarda object olarak saklandığı için dönüşüm gerekebilir (Unboxing). |
Oluşturma |
new anahtar kelimesi zorunlu değildir (int[] arr = {1,2};). |
new anahtar kelimesiyle örneklenmelidir (List<int> list = new List<int>();). |
Boyut |
Oluşturulduktan sonra boyutu sabittir (dinamik olarak değişmez). |
Çoğu koleksiyon dinamik bir yapıya sahiptir, boyutu otomatik olarak artar/azalır. |
Boyutluluk |
Çok boyutlu olabilir (int[,] matris). |
Genellikle tek boyutludur, ancak iç içe koleksiyonlar ile çok boyutlu veri yapıları oluşturulabilir. |
Koleksiyon Arayüzleri: Ortak Davranışlar
C# koleksiyonları, belirli davranışları ve yetenekleri tanımlayan bir dizi arayüz (interface) üzerinde kurulmuştur. Bu arayüzler, farklı koleksiyon türlerinin benzer yollarla kullanılmasını sağlar:
- ICollection: System.Collections altında yer alan tüm koleksiyonların temel özelliklerini ve metotlarını tanımlar. Koleksiyonun eleman sayısını (Count) ve senkronizasyon durumunu yönetir.
- IEnumerable: Bir koleksiyonun içindeki elemanlar üzerinde döngü (foreach) yapabilmenizi sağlayan GetEnumerator() metodunu içerir. Tüm koleksiyonların temelidir.
- IEnumerator: IEnumerable tarafından sağlanan numaralandırıcıdır. Koleksiyonlardaki her bir elemana teker teker erişmeyi sağlar (MoveNext(), Current, Reset()).
- IComparer: İki nesneyi karşılaştırmak için kullanılan metotları (Compare()) tanımlar. Sıralama işlemleri için özelleştirilmiş karşılaştırma mantığı yazmanızı sağlar.
- IDictionary: Anahtar-değer çiftleri (key-value pairs) mantığıyla çalışan koleksiyonlar için temel arayüzdür. Hashtable gibi koleksiyonlar bu arayüzü uygular.
- IList: Koleksiyondaki elemanlara indeks numarası ile erişimi ([index]) ve sıralı eleman yönetimi için metotları (Add(), Remove(), Insert()) sağlar. ArrayList gibi koleksiyonlar bu arayüzü uygular.
Koleksiyon Grupları: Generic ve Non-Generic
.NET Framework'te koleksiyonlar temel olarak iki ana gruba ayrılır:
- Non-Generic Koleksiyonlar: System.Collections namespace'i altında bulunurlar. Her türden (object olarak) eleman saklayabilirler. Bu esneklik, beraberinde tip güvenliği sorununu ve performans düşüşünü (boxing/unboxing) getirir.
- ArrayList (IList uygular)
- Hashtable (IDictionary uygular)
- SortedList (IDictionary ve IList uygular)
- Queue (Non-Generic)
- Stack (Non-Generic)
- BitArray (Bit dizileri için)
- Generic Koleksiyonlar: System.Collections.Generic namespace'i altında bulunurlar. Belirli bir veri türünü (<T>) saklamak üzere tasarlanmıştır. Bu sayede tip güvenliği sağlanır ve object tipine dönüşüm (boxing) yapılmadığı için performansları daha iyidir. .NET 2.0 ile hayatımıza girmişlerdir.
- List<T>
- Stack<T> (LIFO)
- Queue<T> (FIFO)
- LinkedList<T>
- Dictionary<TKey, TValue>
- SortedDictionary<TKey, TValue>
- SortedList<TKey, TValue> (Generic)
Popüler Koleksiyonların İncelenmesi
Şimdi gelin, en sık kullandığımız bazı koleksiyonlara yakından bakalım:
1. ArrayList (Non-Generic)
ArrayList, esnek boyutlu bir dizidir ve IList arayüzünü uygular. İçine her türden eleman ekleyebilirsiniz, çünkü tüm elemanlar object tipinde saklanır. Bu da boxing/unboxing maliyetine yol açar.
using System.Collections;
ArrayList al = new ArrayList();
al.Add("Webkolog"); // String ekledik
al.Add(123); // Integer ekledik
al.Insert(0, "Merhaba"); // Belirli bir indekse ekleme
Console.WriteLine(al.Count); // Eleman sayısı: 3
// Elemana erişim ve unboxing
// ArrayList'ten bir elemanı okurken, orijinal tipine dönüştürmeniz (unboxing) gerekir.
string yazi = (string)al[1]; // Index 1'deki eleman olan "Webkolog"
Console.WriteLine(yazi);
al.Remove("Webkolog"); // Değere göre silme
al.RemoveAt(0); // İndekse göre silme
ArrayList karmaşık veri tiplerini barındırabilir (örneğin, 0 => "yazı", 1 => 12). Ancak, List<T> gibi Generic koleksiyonlar, tip güvenliği ve performans avantajları nedeniyle ArrayList'in yerine tercih edilmelidir.
2. BitArray (Non-Generic - Specialized)
BitArray, adından da anlaşılacağı gibi, yalnızca true veya false (yani bit) değerlerini saklayan özel bir koleksiyondur. Mantıksal (And, Or, Xor, Not) işlemleri gerçekleştirebilme yeteneğine sahiptir. Dinamik bir yapıya sahiptir ve indekslenebilir.
using System.Collections;
// Boolean dizisinden BitArray oluşturma
bool[] bitler = { true, false, true, true, false };
BitArray bitDizisi1 = new BitArray(bitler);
Console.WriteLine("Bit Sayısı: " + bitDizisi1.Count); // Çıktı: Bit Sayısı:5
// Byte dizisinden BitArray oluşturma (her byte 8 bit olarak temsil edilir)
byte[] Sayilar = { 18, 56, 21 }; // 18 = 00010010, 56 = 00111000, 21 = 00010101
BitArray bitDizisi2 = new BitArray(Sayilar);
Console.WriteLine("Bit Sayısı: " + bitDizisi2.Count); // Çıktı: Bit Sayısı:24
// Belirli bir boyutta BitArray oluşturma
BitArray bitDizisi3 = new BitArray(20); // 20 tane false bit
BitArray bitDizisi4 = new BitArray(20, true); // 20 tane true bit
// Mevcut BitArray'den yeni bir BitArray oluşturma
BitArray bitDizisiKopya = new BitArray(bitDizisi1);
// Count ve Length Özellikleri
bool[] ornekBitler = { true, false, true, true, false, true };
BitArray ornekBitDizisi = new BitArray(ornekBitler);
Console.WriteLine("Bit Sayısı (Başlangıç):" + ornekBitDizisi.Count); // Çıktı: Bit Sayısı:6
// BitArray'in uzunluğunu değiştirme
ornekBitDizisi.Length = 4;
Console.WriteLine("Bit Sayısı (Yeni):" + ornekBitDizisi.Count); // Çıktı: Bit Sayısı:4
// Uzunluk küçültüldüğünde yüksek değerlikli bitler kaybolur.
// Mantıksal Metotlar (Not, And, Or, Xor)
static void EkranaYaz(BitArray ba)
{
Console.WriteLine();
for (int i = 0; i < ba.Count; i++)
{
Console.Write("{0,-6}", ba[i]);
if ((i + 1) % 8 == 0) // 8 bitlik bloklara ayırarak daha okunaklı çıktı
Console.WriteLine();
}
}
byte[] sayilarMantık = { 18, 26 }; // 18 (00010010), 26 (00011010)
BitArray bitDizisiMantık = new BitArray(sayilarMantık);
Console.WriteLine("\nOrijinal Bitler:");
EkranaYaz(bitDizisiMantık);
BitArray Not_bitDizisi = bitDizisiMantık.Not(); // Mantıksal DEĞİL
Console.WriteLine("\nNOT işlemi sonrası:");
EkranaYaz(Not_bitDizisi);
// Set() ve SetAll() Metotları
BitArray setBitDizisi = new BitArray(new bool[] { true, false, true, true, false, true });
setBitDizisi.Set(0, false); // 0. indeksteki biti false yap
Console.WriteLine("\nSet(0, false) sonrası:");
foreach (bool bit in setBitDizisi)
Console.Write("{0,-6}", bit);
setBitDizisi.SetAll(true); // Tüm bitleri true yap
Console.WriteLine("\nSetAll(true) sonrası:");
foreach (bool bit in setBitDizisi)
Console.Write("{0,-6}", bit);
// Get() Metodu
bool deger = setBitDizisi.Get(0); // 0. indeksteki bitin değerini al
Console.WriteLine("\n0. indeksteki bitin değeri: " + deger); // Çıktı: True (çünkü SetAll(true) yapıldı)
3. Dictionary<TKey, TValue> (Generic)
Dictionary<TKey, TValue>, anahtar-değer (key-value) çiftlerini saklayan, çok hızlı arama ve erişim sağlayan bir koleksiyondur. Anahtarlar benzersiz olmalıdır.
using System.Collections.Generic;
using System.Windows.Forms; // MessageBox için
Dictionary
4. Queue<T> (Generic - FIFO)
Queue<T>, "İlk Giren İlk Çıkar" (First In First Out - FIFO) mantığıyla çalışan bir koleksiyondur. Gerçek hayattaki kuyruklar gibi, ilk eklenen eleman ilk çıkarılır.
using System.Collections.Generic;
Queue
5. Stack<T> (Generic - LIFO)
Stack<T>, "Son Giren İlk Çıkar" (Last In First Out - LIFO) mantığıyla çalışan bir koleksiyondur. Üst üste yığılmış tabaklar gibi, son eklenen eleman ilk çıkarılır.
using System.Collections.Generic; // Generic Stack için
Stack
6. Hashtable (Non-Generic - IDictionary)
Hashtable, Dictionary<TKey, TValue>'ın non-generic karşılığıdır ve anahtar-değer çiftlerini saklar. Performansı anahtar türüne ve hashing algoritmasına bağlıdır.
using System.Collections;
Hashtable htablo = new Hashtable();
htablo.Add("01", "Adana");
htablo.Add("06", "Ankara");
htablo.Add("34", "İstanbul");
Console.WriteLine(htablo.ContainsKey("06")); // Çıktı: True
Console.WriteLine(htablo.ContainsValue("İstanbul")); // Çıktı: True
Console.WriteLine(htablo["34"]); // Key ile değere erişim: İstanbul
htablo.Remove("01"); // Key'e göre silme
Console.WriteLine(htablo.Count); // Eleman sayısı: 2
7. SortedList (Non-Generic - IDictionary ve IList)
SortedList, anahtar-değer çiftlerini saklar ve anahtarlara göre sıralı tutar. Hem anahtar hem de indeks ile elemanlara erişim imkanı sunar.
using System.Collections;
SortedList sl = new SortedList();
sl.Add("A", "Ali");
sl.Add("C", "Ceren");
sl.Add("B", "Baki"); // Eklendiğinde alfabetik sıraya göre yerleşir
Console.WriteLine(sl["B"]); // Key ile erişim: Baki
Console.WriteLine(sl.GetByIndex(1)); // İndeks ile erişim: Baki (Çünkü A -> Ali, B -> Baki, C -> Ceren şeklinde sıralanır)
sl["D"] = "Deniz"; // Indexer ile ekleme/güncelleme
sl.Remove("C"); // Key ile silme
Console.WriteLine(sl.ContainsKey("A")); // True
Console.WriteLine(sl.ContainsValue("Deniz")); // True
8. SortedDictionary<TKey, TValue> (Generic)
SortedDictionary<TKey, TValue>, Dictionary<TKey, TValue>'ın sıralı versiyonudur. Anahtar-değer çiftlerini anahtarlarına göre artan sırada tutar.
using System.Collections.Generic;
SortedDictionary
9. List<T> (Generic)
List
Sonuç
Koleksiyonlar, C# programlamasında verileri düzenlemenin ve üzerinde işlem yapmanın temelini oluşturur. Hangi koleksiyonu seçeceğiniz, uygulamanızın gereksinimlerine (veri türü, sıralama ihtiyacı, erişim hızı, bellek kullanımı vb.) bağlıdır. Genel bir kural olarak, mümkün olduğunca Generic koleksiyonları (List<T>, Dictionary<TKey, TValue> vb.) tercih etmeniz, hem tip güvenliği hem de performans açısından en iyi yaklaşımdır. Non-Generic koleksiyonlar, .NET'in eski versiyonlarıyla uyumluluk veya object tipiyle çalışma ihtiyacı gibi belirli senaryolarda hala kullanılabilir olsa da, yeni projelerde nadiren tercih edilirler.
Umarım bu makale, koleksiyonların geniş dünyasına keyifli bir giriş yapmanızı sağlamıştır. Veri yapıları ve algoritmaları programlamanın kalbidir; bu konuyu ne kadar iyi kavrarsanız, o kadar yetenekli bir geliştirici olursunuz!
Webkolog'u takipte kalın!
Hepinize bol kodlu ve keyifli öğrenme süreçleri dilerim!
0 yorum:
Yorum Gönder