KolekcjeCzęść NR.10Tablice są pożyteczne i występują w większości języków programowania . Jednak jak pisałem wcześniej mają one pewne ograniczenia. Jak np. ustalona wielkość tablicy bez możliwości jej zmiany, bez tworzenia nowej tablicy. Oczywiście tablice nie są jedynym sposobem na przechowywanie zbioru elementów.


W tym wpisie przejrzymy kolekcje klas z przestrzeni nazw System.Collections. Aby używać tych kolekcji musisz dodać do kodu w bloku using przestrzeń nazw “System.Collections”. Możesz też napisać jedną z kolekcji i skorzystać z pomocnika w Visual Studio.

Pamiętaj o dodaniu przestrzeni nazw w using

Tablica może przechowywać tylko typy wartościowe . W tym czasie klasy kolekcji przetrzymują elementy jako obiekty.

Co to znaczy?

Najlepiej to zobrazować na tablicy int i tablicy object.

Int jest typem wartościowym, więc wartości są przetrzymywane bezpośrednio i nie ma tu żadnych dodatkowych operacji.

klass typy referencyjne-15
Teraz co się dzieje, gdy mam tablicę obiektów. Dodając int do tablicy object automatycznie ta wartość jest pakowana(boxing) i elementy w tablicy są powiązanie referencyjne z zapakowaną zawartością. Oczywiście by wyciągnąć dane z takiej tablicy musi wykonać się odpakowanie(unboxing) poprzez rzutowanie.

klass typy referencyjne-14
Tablice są wręcz stworzone do przechowywania typów wartościowych. Natomiast klasy kolekcji przechowują wszystko, jako obiekty, więc trzeba zawsze wykonać rzutowanie. Może to być wadą,albo zaletą w zależności, jak się na to spojrzy. W końcu w kolekcjach nie trzeba podawać typu.
Różnice pomiędzy tablicami a kolekcjami:

  • Tablica deklaruje typ elementów, które przechowuje. Kolekcja tego nie robi, ponieważ kolekcja przechowuje elementy jako obiekty.
  • Tablica ma określony rozmiar i nie można jej powiększyć ani zmniejszyć. Kolekcja w zależności od potrzebny dynamicznie dostosowuje automatycznie rozmiar.
  • Tablica może być wielowymiarowa. Kolekcja nie może być wielowymiarowa. Jednak kolekcje mogą przechowywać wewnątrz siebie inne kolekcje.

Kolekcja ArrayList

ArrayList można traktować jak ulepszenie tablicy. Jeśli irytowały cię pewne wady tablicy, to ta klasa większość ich rozwiąże . Potrafi ona:

  • Usuwać wybrane elementy z kolekcji przy użyciu metodyRemove(). Elementy automatycznie ustawią się w wybrane miejsca.
  • Za pomocą metody RemoveAt() usuwa się dany element przy pomocy jego indeksu.
  • Dodawać kolejne nowe elementy za pomocą metody Add(). W razie potrzeby ArrayList automatycznie zmieni swój rozmiar.
  • Dodawać elementy w środku zbioru elementów za pomocą metody "Insert().
  • Może się odnieść referencyjnie do istniejącego elementu w ArrayList za pomocą nawiasów kwadratowych i numery indeksu elementu.

Oto przykład jej użycia:

ArrayList liczby = new ArrayList();

//przykład meotdy Add for (int i = 0; i < 10; i++)
{   
    liczby.Add(i);
}

//Przykład metody Remove() int usuneL = 1;
liczby.Remove(usuneL);

//Przykład metody RemoveAt() liczby.RemoveAt(7);

//Przykład Insert() int dodamL = 121;
liczby.Insert(3, dodamL);


Wynik działania kodu:

Rezultat kodu

Jak widzisz kasowanie i dodawanie elementów nie stworzyło bałaganu w ArrayList.

ArrayList posiada jeszcze wiele innych fajnych możliwości jak kasowanie elementów od - do za pomocą RemoveRange(). Wyszukiwanie indeksu wybranego elementu za pomocąIndexOf().

Czy czyszczenie zawartości za pomocą metody Clear()? oraz możliwość kopiowania zawartości do tablicy obiektów za pomocą metody ToArray().Muszą to być obiekty, ponieważ ArrayList tak przechowuje elementy bez określenia ich typów. Czyli ArrayList może przechowywać wiele różnych elementów.

int poszukiwany = 9;
int indeks = liczby.IndexOf(poszukiwany);

liczby.RemoveRange(0, 4);
object[] tablicaObiektów = liczby.ToArray();            

liczby.Clear();
int wielkosc = liczby.Count;


Tak jak mówiłem wielkość ArrayList jest ustalana dynamicznie. Wyniku czyszczenia wielkości ArrayList zmieniła się do wartości zero.

Count ArrayList dynamicznie

Kolekcja Queue (Kolejka)

Klasa Queue implementuje mechanizm FIFO. Element umieszczony w kolejce zawsze znajduje się na jej końcu, a element wychodzący jest pobierany z jej początku. Niczym kolejka ludzi np. po zakupy. Osoba przychodząca do kasy w sklepie ustawia się w kolejce , a osoba na jej początku realizuje swoje płatności.

Myślę , że nie powinieneś mieć problemów ze zrozumieniem jak działa kolejka. Takie gotowe zachowanie kolekcji może być przydatne w niektórych rozwiązaniach w programowaniu.

Queue kolejka = new Queue();

for (int i = 0; i < 10; i++)
{
    kolejka.Enqueue(i*i);
    Console.WriteLine("Dodany:" + (i*i).ToString())
}

while (kolejka.Count > 0)
{
    Console.WriteLine("Wyciągnięty:" + kolejka.Dequeue());
}

Wyciąganie elementów z kolejki odbywa się za pomocą metody “Dequeue” ,a za pomocą metody “Enqueue” elementy trafiają do kolejki.

Moim zdaniem np. gdybyśmy chcieli napisać aplikację teleturniejową z pytaniami, które nie mogą się powtarzać , a zarazem musiałyby one trafiać na koniec kolekcji pytań (np.w wypadku błędnej odpowiedzi), to kolejka fantastycznie nadawałaby się do tego celu.

Kolekcja Stack (Stos)

Kolekcja Stack implementuje mechanizm LIFO. Element jest dodawany na spód stosu , a elementy opuszczane są też ze spodu stosu.

Pomyśl o stosie jako o książkach, które są ułożone jedna na drugiej. Nie możesz wyciągnąć książki ze środka , możesz wyciągać książkę zaczynając od spodu stosu. W ten sposób do stosu możesz dodawać książki tylko na górze zbioru.

Za pomocą metody Push() dodajemy elementy do stosu , a za pomocą metody Pop() elementy te wyciągamy.


Stack stos = new Stack();

foreach (var item in new int[4] {1,9,8,7})
{
    stos.Push(item);
}
object[] tempBef = stos.ToArray();
ArrayList temp = new ArrayList();

while (stos.Count > 0)
{
    temp.Add(stos.Pop());
}

Oto wynik kodu. Skorzystałem tutaj z tablicy obiektów i ArrayList , by pokazać jak elementy są ułożone i jak są wyciągane.

stos działanie

Elementy ułożyły się faktycznie jak stos w czasie dodawania.

Kolekcja HashTable

Jak do tej pory widziałeś tablice są mapowane za pomocą indeksów w postaci liczb całkowitych. Jednak do dyspozycji mamy też tablice, w których indeksy mogą być innego typu, jak np. string, double, czy nawet DateTime.

HashTable jest taką tablicą. Jej zawartość składa się z dwóch tablic . Jedna tablica przechowuje klucze druga wartości przypisane tym kluczom. Klucze nie mogą się powtarzać w HastTable. Gdy taka sytuacja nastąpi zostanie rzucony wyjątek. Na szczęście do dyspozycji mamy metodę ContainsKey(), która może sprawdzić, czy dany klucz już istnieje.

ContainsKeys
Przykład działania HashTable.

Hashtable rokWydania = new Hashtable();

rokWydania["Mortal Kombat"] = 1992;
rokWydania["DefenderOfCrow"] = 1988;
rokWydania["Gobliins"] = 1991;

ArrayList dane = new ArrayList();

foreach (DictionaryEntry item in rokWydania)
{
    string nazwa = (string)item.Key;
    int rok = (int)item.Value;
    dane.Add(string.Format("Name: {0}, Date {1}",nazwa,rok));
}

Zauważ , że w pętli foreach elementy wyciągane z Hashtable są klasami DictionaryEntry. Klasa ta zawiera dwie właściwości Key i Value, które pozwalają uzyskać dostęp do wartości, bądź klucza w Hashtable. Klucz i wartości są obiektami.

Rezultat kodu:
HASHTABLE
 
Oczywiście możemy oddzielnie wyświetlić klucze i wartości.

foreach (var item in rokWydania.Keys)
{
    dane.Add((string)item);
}

foreach (var item in rokWydania.Values)
{
    dane.Add(item.ToString());
}
Dalszy rezultat:
HASHTABLE 2

 

Kolekcja SortedList


SortedList działa podobnie jak Hashtable, więc też mamy do czynienia z kluczami i wartościami. Zasady jej użycia też są te same

Ta kolekcja jest jednak sortowana. Jak to działa?

Kiedy umieszczasz klucz/wartości do SortedList, klucze są umieszczone w swojej tablicy posortowane. Czyli każde umieszczenie klucz/wartości powoduje sortowania umieszczonego klucza w odpowiednim miejscu. W trakcie sortowania program pilnuje, aby klucze i wartości były odpowiednio synchronizowane, więc sortowanie nie uszkodzi kolekcji.

SortedList rokWydania2 = new SortedList();

rokWydania2["Mortal Kombat"] = 1992;
rokWydania2["DefenderOfCrow"] = 1988;
rokWydania2["Gobliins"] = 1991;

ArrayList dane2 = new ArrayList();

foreach (DictionaryEntry item in rokWydania2)
{
    string nazwa = (string)item.Key;
    int rok = (int)item.Value;
    dane2.Add(string.Format("Name: {0}, Date {1}", nazwa, rok));
}

Jak widać SortedList jest posortowana w kolejności alfabetycznej.

SortedList

Gotowe wartości w kolekcjach

Tak jak w tablicach możesz umieścić gotowe wartości do kolekcji przy jej inicjacji. Jest to lepsza alternatywa niż kilka linijek Add().

ArrayList liczby = new ArrayList() {1,2,34,56};

SortedList rokWydania3 = new SortedList() {
                {"Goblins",1991},
                {"Mortal Kombat",1992}
           };

Tak naprawdę kompilator za ciebie wywołuje metodę Add(), by dodawać te wartości. Dlatego taka inicjacja nie działa dla stosu i kolejki.

kolejka bez add

Dla bardziej złożonych typów jak SortedList i Hashtable tworzysz parę kluczy i wartości , jako typ anonimowy.

Tyle o kolekcjach


Do pełnej wiedzy z zakresu, jak przechowywać zbiory danych musisz jeszcze tylko znać parę klas z System.Generics ,ale o tym później. Te klasy zachowują się jeszcze inaczej niż tabele i kolekcje.

BitArray


W System.Collection można znaleźć BitArray, ale szczerze mówiąc nie znam, na razie sensownego zastosowania.