24 Mayıs 2013 Cuma

C# koleksiyonlar



Collection’lar veriyi saklamak,gruplamak ve içerisinde gezinmek için kullanılırlar.Array’ler de bu işe yarar fakat collection’lar array’lerin geliştirilmiş versiyonudur.

System.Collections namespace’i : Arraylist, Collection interfaces, iterators, Hastable, CollectionBase sınıfı,ReadOnlyCollectionBase sınıfı, DictionaryBase sınıfı, DictionaryEntry sınıfı, Comparer sınıfı, Queue sınıfı, SortedList, BitArray, Stack,

System.Collections.Specialized namespace’i : Specialized String sınıfları, Specialized Dictionary, NameValueCollection sınıfı, CollectionsUtil , BitVector32 structure içerir

System.Collections.Generic namespace’i : Collection.Generic interfaces, Generic Dictionary, Generic Comparer, GenericEquality Comparer, Generic KeyValuePair Structure, Generic List,Generic List.Enumerator structure, Generic Sorted List, Generic Queue, Generic SortedDictionary, Generic LinkedList, Generic Stack içerir.



Kendi Sort sınıfınızı yazmak :
Normalde arrayler ve listelerin standart Compare metodu vardır bu metod iki değeri karşılaştırır ve birinci değer büyük ise -1 , ikinci değer büyükse 1 , eşitse 0 döndürür. Biz IComparer interface’ini implemente etmiş kendi sınıfımızı yazıp daha sonra Sort metoduna bu Comparer’ı verirsek artık sort işlemi bizim belirlediğimiz kriterlere göre karşılaştırma yapar. Aşağıdaki örnek Comparer tersten sıralamayı anlatmaya çalışmaktadır.

public class DescendingStringComparer : IComparer
{
CaseInsensitiveComparer _comparer = new CaseInsensitiveComparer();

#region IComparer Members
public int Compare(object x, object y)
{
//Bu kod sayesinde (x,y değil y,x) testen sıralama yapılır.
return _comparer.Compare(y, x);
}
#endregion
}

list.Sort(new DescendingStringComparer());
şeklinde bir listeyi sort edersek bu sefer küçükten büyüğe değil büyükten küçüğe sıralama yapacaktır.

Sequential Lists:Queue : Kuyruk FIFO ilk giren ilk çıkar şeklinde çalışır. Bir kuyruğa yeni bir eleman eklemek için
Queue q = new Queue();
q.Enqueue("a");
q.Enqueue("b"); geriye bir değer döndürmez.

Kuyruktan bir eleman çıkarmak için ise q.Dequeue(); kullanılır ve ilk eleman listeden çıkarılır yani a çıkarılır ve geriye object olarak çıkardığı eleman döner. Eğer kuyruktan eleman çıkarmadan sıradaki elemanla ilgili işlem yapılmak isterse Peek() metodu kullanılabilir.
if (q.Peek() is String)
q.Dequeue();

Stack: Kuyruğun tam tersi mantıkta çalışır LIFO ilk giren son çıkar mantığıyla çalışır.
Stack’a eleman eklemek için Push çıkarmak için Pop metodu kullanılır.

Stack s = new Stack ();
s.Push("a");
s.Push("b");
object o = s.Pop();
//Pop metodundan çıkarılan eleman döner.

Dictionaries:

Hastable : Key-Value şeklinde veri saklamak için kullanılır. Bir dataya bir anahtar yardımıyla erişmek için Hashtable çok kullanışlıdır ancak eleman sayısı az olan listelerde Hastable yerine ListDictionary kullanmak performans açısından daha etkilidir.Hashtable’a veri eklemek ve listelemek aşağıdaki gibidir.

Hashtable h = new Hashtable();
h["adi"] = "koray";
h["mail"] = "koraykirdinli@yahoo.com";
foreach (DictionaryEntry item in h)
{
listBox4.Items.Add(item.Key +" : "+ item.Value);
}


DictionaryEntry key/value çiftlerini saklamak için kullanılan bir konteyner nesnedir. Bütün Dictionary sınıfları IDictionary interface’ini implement eder. IDictionary interface’inin önemli property ve metodları şunlardır.
IsFixedSize,IsReadOnly,Item,Keys,Values – Add,Clear,Contains,GetEnumarator,Remove.

IDictionary interface’inde IList interface’inden farkı IList nesnelere indexi ile IDictionary nesneleri anahtar değeri ile erişir.
Hashtable listelerde bir key veya Value değerinin var olup olmadığını anlayabilmek için iki metod vardır:ContainsKey,ContainsValue. Hashtable listede aynı keye sahip birden fazla key olamaz.
Eğer Hashtable’a key olarak bir sınıf nesnesi vererek eklersek bu sefer Name’leri aynı olsa bile farklı olarak algılar.

Person p1 = new Person("Koray");
Person p2 = new Person("Koray");
Hashtable h2 = new Hashtable();
h2[p1] = "a";
h2[p2] = "a";
//Bu sefer Person sınıfının Equals ve GetHashCode metodlarını override ettiğimiz için
//bütün özellikleri aynı olsa da farklı iki eleman olarak alır.

public class Person

{
public string Name;
public Person(string name)
{
Name = name;
}
public override bool Equals(object obj)
{
Person other = obj as Person;
if (other == null) return false;
return other.Name == Name;
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
//----------------------------------------------------------------------------------

public class InsensitiveComparer : IEqualityComparer
{
//Bu comparer karşılaştırmayı case-insensitive olarak yapmayı sağlar.
CaseInsensitiveComparer _comparer = new CaseInsensitiveComparer();

public int GetHashCode(object obj)
{
return obj.ToString().ToLowerInvariant().GetHashCode();
}

public new bool Equals(object x, object y)
{
if (_comparer.Compare(x, y) == 0)
{
return true;
}
else return false;
}
}

Hashtable h3 = new Hashtable(new InsensitiveComparer());

h3["aa"] = "a";
h3["Aa"] = "a";
h3["AA"] = "a";
Console.WriteLine(h3.Count);// insensitive comparer kullandığımız için 3 değil 1’dir.

//----------------------------------------------------------------------------------

SortedList:
//SortedListlerde Hashtable'a ek olarak bir elemana hem key ile hem de index ile //erişebiliriz. Elemanı eklediğimiz anda listede uygun yere ekleme yapar.
SortedList list = new SortedList();
list["First"] = 1;
list["Second"] = 2;
list["first"] = "1";
//Yukardaki listede indexler bu şekilde oluşur. 0:first,1:First,2:Second

list.GetByIndex(2);
foreach (DictionaryEntry item in list)
{
listBox5.Items.Add(item.Key+" : " +item.Value);
}

Özelleştirilmiş Dictionary’ler:

Eleman sayısı 10 dan az ise Hastable yerine ListDictionary kullanmak performans açısından daha iyidir.ListDictionary Hastable ile aynı interface’leri implement eder , kullanımı da aynıdır.

HybridDictionary: Eğer bir listenin eleman sayısının az veya çok olduğunu önceden kestiremiyorsak HybridDictionary kullanılmalıdır. Interface’leri ve kullanımı yine aynıdır.

OrderedDictionary: Keyleri sıralı bir listeye ihtiyacınız olduğunda Hastable işe yaramaz ayrıca bir elemana indexi ile erişmekte mümkün olmaz. SortedList ise keyler beklediğiniz gibi sıralanmıyorsa OrderedDictionary kullanılabilir. Ekstra birkaç özelliği daha vardır.

Özelleştirilmiş tipi olan Koleksiyonlar.

BitArray : true , false tutmak için kullanılır ve elemanları Xor , And gibi bitsel işlemler yapılabilir.
BitVector32 : bitleri 32 bit integer değer olarak saklamaya yarar.
StringCollection : ArrayList gibidir ancak sadece string içerir.



Bir Hashtable veya SortedList’i case insensitive yapmak için aşağıdaki şekilde yaratabiliriz.
Hashtable ht= CollectionsUtil.CreateCaseInsensitiveHashtable();
SortedList sl = CollectionsUtil.CreateCaseInsensitiveSortedList();

Normal şartlarda karşılaştırma işlemi o anki thread’in bölgesel ayarına göre yapılır ancak bunu değiştirmek aşağıdaki kod ile mümkün.
Hashtable ht2 = new Hashtable(StringComparer.InvariantCulture);

NameValueCollection : key/value alır ancak bir keye ait birden fazla value olabilir.
NameValueCollection list = new NameValueCollection();
list.Add("key1", "value1");
list.Add("key1","value2");
list.Add("key2", "value3");
foreach (string s in list.GetValues("key1"))
{
listBox6.Items.Add(s);
}

//----------------------------------------------------------------------------------


GENERIC COLLECTIONS
Generic koleksiyonların daha önce bahsettiğimiz Arralist,HashTable gibi koleksiyonlardan en önemli farkı type-safe yani belli bir tipte olması , boxing unboxing den kurtulmamızı ve dolayısıyla performansımızı artırmamızı sağlar. Böylece run time da istenmeyen hataları da azaltmış oluruz.

public class MyList : ICollection, IEnumerable
{
//Bu sınıf sadece T tipinde değere alan bir koleksiyondur. Kendimiz tip güvenli yapmış olduk. T bu sınıf tanımlanırken integer,string vs olarak tanımlanabilir.
ArrayList array = new ArrayList();

public void Add(T val)
{
array.Add(val);
}
public T this[int index]
{
get { return (T)array[index]; }
}
#region ICollection Members
#endregion

#region IEnumerable Members
#endregion
}

//Bu tanımlama sayesinde bu liste sadece integer değer alabilir hale geldi.Arraylistin sadece integer alan versiyonu aşağıdaki gibidir.
List list = new List();
list.Add(1);
//list.Add("2"); ERROR



Generic Delegates:
public delegate int Comparison(T x, T y);

Bunun avantajı örneğin bir listeyi testen yazdıran bir Comparison nesnesi yapmak istiyorsunuz ancak her bir tip için de bunu tek tek yapmak uzun sürecektir. Bunun yerine Comparison delegesi generic olduğundan tek bir metod ile işlem yapılabilir.

Generic queue ve stack da aşağıdaki gibi oluşturulur kullanımı generic olmayan versiyonu ile aynıdır.
Queue queue = new Queue();
Stack stack = new Stack();


Generic Dictionary : Hashtable,ListDictionary ve HybridDictionary koleksiyonlarının generic halidir.
Dictionary list = new Dictionary();
list.Add(1, "koray");
list.Add(2,"kirdinli");
list[3] = "aaaaaa";

//Diğer bazı generic koleksiyonların tanımlanması aşağıdaki gibidir.
SortedList sl = new SortedList();
SortedDictionary sd = new SortedDictionary();
LinkedList ll = new LinkedList();

.NET içerisinde kendi koleksiyonlarımızı yazabilmemiz için kolaylık olması bakımından base sınıflar mevcuttur.
Sınıfımızı CollectionBase’den türetirsek : IList,IEnumerable,ICollection interface lerini ekstra kod yazmadan implement etmiş oluruz.

ReadOnlyCollectionBase: IList,IEnumerable,ICollection interface lerini ekstra kod yazmadan implement eder.Sadece koleksiyona dışarıdan ekleme çıkarma yapılamaz. Örneğin kullanıcı listesi değişmesi istenmiyorsa kullanılabilir.


DictionaryBase:IDictionary,IEnumerable,ICollection ICollection interface lerini ekstra kod yazmadan implement eder.

Hiç yorum yok: