22. Koleksiyonlar

.NET’te doğru koleksiyonu bulmak, bir kamera mağazasında doğru kamerayı bulmak gibidir! Seçilebilecek çok fazla seçenek var ve her biri belirli senaryolarda güçlü, diğerlerinde ise zayıf. .NET’te bir koleksiyon aramak sizi şaşırttıysa, yalnız değilsinizdir.

.NET koleksiyonundaki serinin ilki olan bu yazıda, her C # geliştiricisinin bilmesi gereken 5 temel koleksiyon türünü ele alacağım. Bunlar daha fazla olmasa bile,  kodlama yaparken % 80 – 90 oranında bu  koleksiyonları kullanacağız. 

  • ArrayList
  • HashTable
  • Dictionary
  • HashSet
  • SortedSet

1.ARRAYLIST

İnceleyeceğimiz ilk koleksiyon arraylist list ile çokça karıştırılan bir koleksiyon tipidir. Arraylistin ne olduğunu, verileri nasıl tuttuğunu, nasıl sunduğunu ve kullanımını kısaca anlattıktan sonra, List ile arasındaki farklardan madde madde bahsedeceğim. Arraylist içerisinde eleman tutarken tip ayrımı yapmayan bir koleksiyon tipidir. Yani jenerik bir koleksiyondur. Değişik tipleri içerisinde tutmasıyla da karşımıza arraylisti konuşurken en çok üzerinde durmamız gereken boxing ve unboxing kavramları çıkar.

Nedir boxing ve unboxing?

Değer tipinin referans tipe çevrilmesi boxing
Referans tipin değer tipe çevrilmesi ise unboxingtir.

  Arraylist içerisinde her tipten eleman tutabildiğinden bu elemanları kullanırken sıkça boxing ve unboxing işlemi yapılması gerekir. Arraylistin içerisinde tuttuğu veriler object tipinde tanımlanmıştır. Bu sebeple arraylist’e her veri eklediğiniz durumda boxing işlemi, arrayListten bir veriyi uygun tipe yönlendirdiğinizde de unboxing işlemi yapmış olursunuz. Bu da performansı azaltan bir süreçtir.

  Bunun yanında ArrayList içinde İkili Aramaya dayanan BinarySearch(…) fonksiyonudur. Bu fonksiyon aranan elemanın index değerini geri döndürür.  Özellikle çalışma anında araya eleman ekleme veya aradan eleman kaldırma ve öteleme gibi özellikleri tercih nedeni olmaktadır.

İster boxing ister unboxing işlemi söz konusu olsun, bellek üzerinde stack ve heap bölgeleri arasında yeniden adresleme ve kopyalama işlemi söz konusudur.

static void Main(string[] args)
{
   ArrayList list = new ArrayList();
   list.Add(1);
   list.Add("1");
   list.Add("cagatay");

   foreach (var item in list)
   {
     Console.WriteLine(item);
   }

    Console.ReadLine();

 }


1.a List vs ArrayList

ArrayList Non-Generic’dir

  • Obje tipinde bir kolleksiyondur. İstenilen her tip depolanabilir.
  • Değer veya referans tip olmak üzere her türlü tip depolanabilir.(string, int, employee, object)
  • Veri depolama ve sunma sırasında boxing ve unboxing gerçekleşir.
  • Tip güvenli değildir.
  • Eskidir. Arraylist C#ın jenerik konseptine sahip olmadığı zamanlardan kalmadır.

List generic’dir

  • T tipinde bir kollekssiyondur, T’nin tipini run-timeda belirleyebilirsiniz.
  • T için declare edilen her türlü tip için kullanılabilir.(string, int, employee, object)
  • Boxing ve unboxing işlemi gerçekleşmez.
  • Tip güvenlidir.
  • Yeni bir kolleksiyondur.
  • List<T>  IEnumerable<T>’i implemente eder ve LINQ kolayca kullanılabilir. (herhangi bir cast veya tip çağırımına ihtiyaç duymadan).
2. HASHTABLE

Hashtable aynı ArrayList gibi birçok veriyi depolayan dizi yapısına göre de yüksek arama performansı sunan bir liste şeklidir. Çırpı(hash) algoritması kullanılır. Hashtable’da Arraylist’ten farklı olarak her elemana bir indis değeri verilmesi yerine belirli bir anahtar-değer(key-value) verilir ve tablonun elemanları çağırılırken bu anahtar onun erişim kimliği gibi referans olarak kullanılır.

HashTable Sehirler = new HashTable();
sehirler.Add("A","Adana");
sehirler.Add("B",2);
sehirler.Add("D","Denizli"); 
  • Obje tipinde bir kolleksiyondur. İstenilen her tip depolanabilir.
  • Değer veya referans tip olmak üzere her türlü tip depolanabilir.(string, int, employee, object)
  • Veri depolama ve sunma sırasında boxing ve unboxing gerçekleşir. Bu sebeple Dictionaryden yavaştır.
  • Tip güvenli değildir.
  • Verileri key-value olarak tutar.
3.DICTIONARY

Dictionary sınıfı hashtable gibi eklendiği elemanları key ve value olarak kaydetmemize yarayan bir sınıfdır.Key valueların tiplerini jenerik olarak tanımlayabiliriz. Boyutu klasik arrayler gibi statik değil değişkendir.

 Dictionary<int,string> users = new Dictionary<int,string>(); 
 
  users.Add(0, "Cagatay"); 
  users.Add(1, "Kuthay");
  users.Add(2, "Ömer"); 
  users.Add(3, "Gorkem"); 
  users.Add(4, "Fukan"); 
  users.Add(5, "Emir");

 foreach (KeyValuePair user in users) 
 { 

    Console.WriteLine("ID =>"+kullanici.Key+"\t"+"Ad Soyad =>"+kullanici.Value); } 
    string yeniKullanici = "Ekrem AYDEMİR"; 


    bool varMi = kullanicilar.TryGetValue(0, out yeniKullanici); //varsa true yoksa false dondürücek 


     if (varMi) 
     { Console.WriteLine("Bu ID de bir kullanici mevcuttur."); } 
     else
     { kullanicilar.Add(0, yeniKullanici);

   Console.WriteLine("Ekleme işlemi başarıyla gerçekleşmiştir.");
 
}

**HashTable ve Dictionary arasındaki fark

 

** SortedDictionary ve Dictionary arasındaki fark
A SortedDictionary binary search tree olarak implemente edilmiştir. Bu sebeple sorted Dictionary üzerinden bir elemana ulaşmanın maliyeti O(lg(n)) dir. Dictionary ise bir hash tabledır, ve bir elemana ulaşmanın maliyeti O(1) dir. SortedDictionary, sıralanacak verilere ihtiyaç duyduğunuzda faydalıdır.

4. HASHSET

HashSet’de veriler matematik dersinden bildiğimiz küme mantığıyla tutulur ve her hashset sanki bir kümeymiş gibi operasyonlar yapılabilir. Matematik dersinden bildiğimiz gibi kümeler için {1,1,1,2} ile {1,2} aynı anlama geliyorsa ve tekrar eden eleman olamaz ise hashSet’te de tekrar eden eleman olamaz. Ayrıca hashset verileri aynı  kümeler gibi sırasız bir şekilde tutar. Hashset için {1,2,3} ve {2,3,1} aynıdır. Bu sebeple hashsetteki verilere indexler ile ulaşamayız.

  static void Main(string[] args)
  {
     HashSet<string> hash = new HashSet<string>();
     HashSet<string> hash2 = new HashSet<string>();

     hash.Add("x");
     hash.Add("y");
     hash.Add("z");
     hash.Add("x");

     foreach (var item in hash)
     {
      Console.WriteLine(hash); // output : x y z 
     }
  
      Console.ReadLine();

   }
  •  Tanımladığımz  iki hashSet’in birbirlerinin alt kümesi veya kapsayan kümesi olup olmadığını kontrol edebiliriz.
   hash.Add("x");
   hash.Add("y");
   hash.Add("z");
   hash.Add("t");

   hash2.Add("x");
   hash2.Add("y");


   Console.WriteLine(hash.IsSupersetOf(hash2));  //true
   Console.WriteLine(hash2.IsSubsetOf(hash));  //true

  •    Kümelerin birbirlerinin kesişimini bir hashsete alabiliriz. Aşağıda hash’in içi x  y olacaktır.
  hash.IntersectWith(hash2);

   foreach (var item in hash)
   {
     Console.WriteLine(item);
   }

  • İki hash’in birbiriyle birleşimini alabiliriz. Hash2 nin içerisi x,y,z,t olacaktır.
    hash2.UnionWith(hash);

   foreach(var item in hash)
   {
     Console.WriteLine(item);
   }


   Console.WriteLine(hash2.Overlaps(hash2));
  •  Hash üzerinde arama yapabiliriz.
    hash.contains(aranan);
  • HashSet içerisindeki elemanları alıp kullanmak istiyorsak linq metotlarını kullanabiliriz.
    var element = hash2.FirstOrDefault(x => x=="x");
  •   Aşağıdaki şekilde bir list tanımlayıp bunu hash yapısına da dönüştürebiliriz.
    List<string> duplicatedEnumrableStrings = new List<string> {"abc", "ghjr", "abc", "abc", "yre", "obm", "ghir", "qwrt", "abc", "vyeu"};
    HashSet<string> uniqueStrings = new HashSet(duplicatedEnumrableStrings);
5.SORTEDSET

SortedSet yapısı C#‘ın generic collection yapılarından biridir. T ile belirttiğimiz tipte elemanlar tutar. C# 4.0 ile hayatımıza girmiş yapılardan biridir. Bu yapının en önemli özelliği listeyi sıralı tutmasıdır. Siz yeni bir eleman eklesenizde silsenizde liste yine sıralı olacaktır.

  SortedSet<int> mySortedList = new SortedSet<int>();

  mySortedList.Add(1);
  mySortedList.Add(9);
  mySortedList.Add(4);
  mySortedList.Add(2);

  foreach (var item in mySortedList)
  {
    Console.WriteLine(item);
  }

SortedSetin verileri sıralı bir şekilde tuttuğunu söyledik. Peki içerisinde bir class nesnesi tutacaksa bu sıralama hangi koşula göre yapılacak buna nasıl karar verir biraz da bunu gözlemleyelim.

  static void Main(string[] args)
  {

   SortedSet<Animal> mySortedList = new SortedSet<Animal>(new AnimalComparer());

   mySortedList.Add(new Animal(21,"cagatay"));
   mySortedList.Add(new Animal(20,"kuthay"));
   mySortedList.Add(new Animal(31,"omer"));
   mySortedList.Add(new Animal(10,"görkem"));
  
   foreach (var item in mySortedList)
   {
     item.Print();
   }


    Console.ReadLine();

  }

  class AnimalComparer : IComparer<Animal>
  {
     public int Compare(Animal x, Animal y)
     {
       // 1: siralamada daha onde
       //-1: siralamada daha geride
       // 0: ayni sirada
       if (x.yas > y.yas)
        return 1;
       else if (x.yas < y.yas)
        return -1;
       else
        return 0;
     }
  }

  class Animal
  {
     public int yas;
     public string name;

     public Animal(int yas, string name)
    {
     this.yas = yas;
     this.name = name;
    }

    public void Print()
    {
       Console.WriteLine("{0} {1}",yas,name);
    }
  }