70-536: Generic Collections

Poniższy artykuł pochodzi z serii Przygotowań do egzaminu 70-536.

Kolekcje generyczne są bezpieczną (ang. type-safe), szybką i wydajną (ang. performance) wersją ich nie-generycznych odpowiedników. Przykładowo List<>, Dictionary<>, Queue<>, Stack<>, SortedList<> i SortedDictionary<> są odpowiednikami zwykłych kolekcji. Różnica polega na tym, że korzystając z typów generycznych podajemy parameter, który jest zamieniany na nazwę typu podczas kompilacji. W ten sposób można użyć każdego typu, ale tylko wcześniej określonego. W innym wypadku otrzymamy błędy już przy kompilacji. Namiastką tego są kolekcje specjalizowane, np. StringCollection.

Dodatkowo w .NET Framework wprowadzono nowe klasy, które nie mają swoich odpowiedników wśród zwykłych typów.

Generyczna Lista

   1: List<int> intList = new List<int>();
   2: intList.Add(1);
   3: //intList.Add("Eastgroup"); // to się nawet nie skompiluje!
   4: intList.Add(2);
   5: intList.Add(3);
   6: int number = intList[0];
   7:  
   8: foreach (int item in intList)
   9: {
  10:     Console.Write("{0} ", item); // 1 2 3
  11: }

W powyższym przykładzie utworzyliśmy listę składającą się ze zmiennych typu int - typów całkowitych. Po jej utworzeniu wykonujemy następujące czynności:

  1. Dodajemy (metoda Add) elementy do listy. Typ musi zgadzać się z tym z deklaracji - w przeciwnym wypadku pojawią się błędy już na poziomie kompilacji.
  2. Odwołujemy się do elementów poprzez ich indeksy - w tym przypadku do elementu o indeksie 0.
  3. Za pomocą pętli foreach iterujemy po elementach listy.

Gdybyśmy chcieli posortować listę w niestandardowy sposób (np. malejąco), możemy wykorzystać metodę Sort i przekazywanego doń delegata. Deklarujemy metodę, która odpowiada typom listy:

   1: static int ReverseIntComparison(int x, int y)
   2: {
   3:     return y - x;
   4: }

Sortujemy listę w kolejności malejącej:

   1: intList.Sort(ReverseIntComparison);
   2: foreach (int item in intList)
   3: {
   4:     Console.Write("{0} ", item); // 3 2 1
   5: }

Kolejka i Stos

Jak już zostało wspomniane wcześniej są to odpowiedniki zwykłych kolekcji o tej samej nazwie. Tworząc ich instancje określamy, jakiego typu zmienne mogą być w nich przechowywane.

   1: Queue<String> que = new Queue<string>();
   2: // Dodajemy element typu String na koniec kolejki
   3: que.Enqueue("Hello");
   4: // Pobieramy pierwszy element z kolejki
   5: String queued = que.Dequeue();

W przypadku stosu:

   1: Stack<int> serials = new Stack<int>();
   2: // Dodajemy element typu int na stos
   3: serials.Push(1);
   4: // Pobieramy ostatnio dodany element ze stosu
   5: int serialNumber = serials.Pop();

Słowniki

Generyczny słownik (ang. Dictionary) odpowiada takim klasom jak Hashtable, ListDictionary, HybridDictionary, OrderedDictionary, NameValueCollection. Słownik przypomina tablice asocjacyjne w innych językach programowania. Wykorzystywany jest do przechowywania par typu klucz/wartość (ang. key/value). Ważną różnicą między generycznym słownikiem a jego odpowiednikim jest to, że wyciągając element za pomocą indeksu, lub iterując po kolekcji, otrzymujemy obiekty typu generycznego KeyValuePair! Typ klucza i wartości zwracanego obiektu będą takie same, jak te wykorzystane w deklaracji słownika.

   1: Dictionary<int, string> countyLookup = new Dictionary<int, string>();
   2: countyLookup[44] = "United Kingdom";
   3: countyLookup[33] = "France";
   4: countyLookup[48] = "Poland";
   5: countyLookup[55] = "Brazil";
   6: //countyLookup["39"] = "Italy"; // Błąd kompilacji!
   7:  
   8: Console.WriteLine("The 48 Code is for: {0}", countyLookup[48]); // The 48 Code is for: Poland
   9:  
  10: foreach (KeyValuePair<int, string> item in countyLookup)
  11: {
  12:     int code = item.Key;
  13:     string country = item.Value;
  14:     Console.WriteLine("Code {0} = {1}", code, country);
  15: }

W powyższym przykładzie utworzyliśmy słownik zawierający kody krajów i ich nazwy. Pokazaliśmy, jak dostać się do wartości za pomocą klucza, oraz dokonaliśmy iteracji po elementach kolekcji. Dla każdego obiektu KeyValuePair wypisaliśmy jego klucz i wartość.

Uporządkowana lista i słownik

Generyczne SortedList i SortedDictionary porządkują elementy w kolekcji na podstawie klucza.

   1: SortedList<string, int> sortList = new SortedList<string, int>();
   2: sortList["Jeden"] = 1;
   3: sortList["Dwa"] = 2;
   4: sortList["Trzy"] = 3;
   5:  
   6: foreach (KeyValuePair<string, int> item in sortList)
   7: {
   8:     Console.Write("{0} ", item); // [Dwa, 2] [Jeden, 1] [Trzy, 3]
   9: }

Sposób użycia SortedDictionary jest identyczny.

   1: SortedDictionary<string, int> sortedDict = new SortedDictionary<string, int>();
   2: sortedDict["Jeden"] = 1;
   3: sortedDict["Dwa"] = 2;
   4: sortedDict["Trzy"] = 3;
   5:  
   6: foreach (KeyValuePair<string, int> item in sortedDict)
   7: {
   8:     Console.Write("{0} ", item); // [Dwa, 2] [Jeden, 1] [Trzy, 3]
   9: }

LinkedList – lista powiązana

Lista powiązana to zbiór elementów, które są ze sobą powiązane. Od jednego elementu możemy przejść do następnego lub poprzedniego.

   1: LinkedList<string> links = new LinkedList<string>();
   2: LinkedListNode<string> first = links.AddLast("First");
   3: LinkedListNode<string> last = links.AddFirst("Last");
   4: LinkedListNode<string> second = links.AddBefore(last, "Second");
   5: links.AddAfter(second, "Third");
   6:  
   7: foreach (string s in links)
   8: {
   9:     Console.Write("{0} ", s); // Second Third Last First
  10: }

Mając listę powiązaną możemy dodawać elementy w następujący sposób:

  • Na początku listy (AddFirst),
  • Na końcu listy (AddLast),
  • Przed pewnym elementem (AddBefore),
  • Po pewnym elemencie (AddAfter).

Elementu nie można dodać wskazując jego indeks!

Nasz własny typ generyczny

   1: using System.Collections;
   2:  
   3: namespace Generics
   4: {
   5:     public class MyList<T> : ICollection, IEnumerable
   6:     {
   7:         private ArrayList _innerList = new ArrayList();
   8:  
   9:         public void Add(T val)
  10:         {
  11:             _innerList.Add(val);
  12:         }
  13:         public T this[int index]
  14:         {
  15:             get
  16:             {
  17:                 return (T)_innerList[index];
  18:             }
  19:         }
  20:  
  21:  
  22:         #region ICollection Members
  23:         // ...
  24:         #endregion
  25:  
  26:         #region IEnumerable Members
  27:         // ...
  28:         #endregion
  29:  
  30:     }
  31: }

Parameter T jest podmieniany nazwą typu podczas kompilacji. Aby korzystać z wyżej podanej kolekcji, należy jeszcze zaimplementować interfejsy ICollection i IEnumerable. Przykład użycia:

   1: MyList<double> myDoubleList = new MyList<double>();
   2: myDoubleList.Add(13.33337);
   3: myDoubleList.Add("Złoooo"); // Błąd na poziomie kompilacji!

 

Podsumowanie

W niniejszym wpisie skupiłem się głównie na wykorzystaniu klas dostarczonych razem z .NET Framework. Osobom żądnym wiedzy radzę poszukać dodatkowych informacji na temat tworzenia własnych typów, generycznych wyliczeń i porównywań.

Kolejny artykuł z serii to 70-536: Serializing Objects

Tagi: , , ,

Comments (1) -

Łukasz
Łukasz Poland
1/28/2010 10:03:55 PM Permalink

ciekawy wpis. Ja w tym semestrze na algorytmach musiałem klepać od podstaw takie kolekcje w c++

Pingbacks and trackbacks (2)+

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading


Eastgroup.pl na facebooku