TabliceCzęść NR.9Tablica pozwala na przechowywanie zbioru wartości. W programowaniu mamy wiele sposobów przechowywania kolekcji wartości ,a tablice są jednym z tych sposobów.
Tablice mogą przechowywać tylko jeden określony typ wartości. NP. tablica int będzie mogła przechowywać tylko liczby tego typu.
W teorii tablica typu object może przechowywać wszystkie typy, ale odpakowywanie(unboxing) wartości z takiej tablicy może okazać się dość kłopotliwe.
Deklaracja tablicy
Deklaracja tablicy wygląda następująco. Po podaniu typu musisz dopisać dwa nawiasy kwadratowe informując w ten sposób kompilator ,że deklarujesz tablicę.
int[] numery;
object[] obiekty;
Instancja Tablic
Sama deklaracja tablicy jeszcze nie daje nam możliwości jej użycia.
Tablice są typami referencyjnymi bez względu na to, jakie wartości one przechowują. Ponieważ są typami referencyjnymi to znaczy , że ich wartości są przechowywane na stercie. Czyli tablica na stosie trzyma tylko referencje/adresy do wartości znajdujących się na stercie.
Podobnie jak z klasą , aby stworzyć instancję tablicy używam słowa kluczowego new.
int[] numery;
numery = new int[3];
object[] obiekty = new object[2];
Aby stworzyć instancje tablicy należy użyć słowa kluczowego new po czym zadeklarować typ danych zgodnych z tym zadeklarowanym i pomiędzy nawiasami kwadratowymi wpisać wielkość tablicy.
Tablica Numery wraz z 0 będzie przechowywać 3 liczby, a tablica Obiekty wraz z liczbą 0 będzie przechowywać 2 obiekty.
Tworzenie tablicy też inicjuje wszystkie wartości w tablicy do ich domyślnych wartości. Dla int będzie to liczba 0 dla object będzie to null.
Wielkość tablic nie musi być z góry podana w programie. Np. użytkownik może podać jej wielkość w czasie wykonywania programu. Wielkość tablicy może być nawet równa zero. Naturalnie wielkość tablicy nie może być liczbą ujemną i nie może być absurdalnie wielka.
Najważniejszą rzeczą przy tablicach jest fakt , że gdy tablica zostanie już zainicjowana z tą wielkością nie może być ona już zmieniona. Co prawda, można wielkość zmienić poprzez ponowną inicjację , ale w ten sposób skasujemy wszystkie wartości przechowywane w tablicy. Ale nie martw się tym faktem, ponieważ w .NET mamy do dyspozycji inne mechanizmy przechowywania zbioru elementów niż tablica.
Inicjalizowanie wartości w tablicy
Kiedy tworzysz instancje tablicy wszystkiej jej elementy mają domyślne wartości. Oczywiście możesz podać swoje wartości w trakcie inicjacji. Podajesz je w nawiasach klamrowych zaraz po stworzeniu instancji.
int[] dawaj = new int[3] { 1, 2, 3};
int[] dawaj2 = new int[] { 1, 2, 3, 4, 6, 7, 8, 9 };
Jak widać nie trzeba podawać wielkości tablicy, gdy dodajemy swoje własne elementy , kompilator robi to za nas.
Jednak jeśli zadeklarowaliśmy wielkość tablicy liczba parametrów nie może być ani większa , ani mniejsza.
Iteracja w Tablic
Za pomocą pętli for możesz np. wyświetlić wszystkie wartości z danej tablicy.
int[] numery = new int[3] { 9, 8, 7 };
for (int i = 0; i < 3; i++)
{
Console.WriteLine(numery[i]);
}
Jednak do użycia tablic w pętli for musimy znać jej wielkość. Jak można ją pobrać?
Wszystkie tablice są z instancją klasy System.Array dzięki temu mamy do dyspozycji kilka przydatnych właściwości i metod.
Jedną z przydatnych właściwości jest właściwość Length określająca wielkość tablicy.
Skoro użytkownik może podać dynamicznie wielkość tablicy dobrze byłoby tą wielkość jakoś później przechwycić.
for (int i = 0; i < numery.Length; i++)
{
Console.WriteLine(numery[i]);
}
Oto przykład kodu, który wyświetla wartości z tablicy w odwrotny sposób.
int[] numery = new int[3] { 9, 8, 7 };
for (int i = numery.Length - 1; i >= 0; i--)
{
Console.WriteLine(numery[i]);
}
Zauważ , że zaczynam pętle od wartości Length – 1 to dlatego , że elementy tablicy są liczone od 0. Czyli wielkość tablicy równa się 3 , ale ostatni element w tablicy ma nr 2. Warto o tym pamiętać, ponieważ podanie niewłaściwego indeksu do tablicy kończy się wyjątkiem.
Innym sposobem wyświetlenia elementów z tablicy jest pętla foreach. Pętla foreach automatycznie przechodzi po wszystkich elementach każdej kolekcji. Pętla jednak może być nieprzydatna w algorytmach, ponieważ nie ma w niej możliwości określenia, od którego, do którego elementu powinna być przeglądana.
foreach (int numery in numery)
{
Console.WriteLine(numery);
}
W pętli foreach musisz tylko zadeklarować zmienną tego samego typu, co elementy w kolekcji. Zmienna ta w tym przypadku “numery” przechowuje wartość elementu z kolekcji w danej iteracji pętli.
Pętla foreach nie jest doskonała. A raczej jest doskonała ,ale tylko do niektórych przypadków.
Pętla foreach kontra pętla for:
- Pętla foreach zawsze przechodzi po całej tablicy, czy kolekcji.
- Pętla foreach zawsze zaczyna przegląd od zera do końca tablicy. Jeśli chcesz jakieś inne zachowanie musisz użyć pętli for.
- Jeśli musisz znać indeks danego elementu w iteracjach lepiej użyć pętli for.
- Jeśli chcesz zmodyfikować elementy w tablicy musisz użyć pętlifor. Zmienna w pętli foreach jest zawsze tylko do odczytu.
Warto byłoby wspomnieć o zmiennej w pętli foreach. Może ona być zmienną lokalną var.Dzięki temu kompilator ustali za ciebie typ elementów w danej tablicy. Jest to przydatne, gdy nie znasz typów elementów w tablicy.
Tablica może np. zawierać w sobie anonimowe obiekty.
var osoby =new[]
{
new {Name="Zdzisław",Wiek=22},
new {Name="Kaśka",Wiek=19}
};
foreach (var osoba in osoby)
{
Console.WriteLine(osoba.Name);
Console.WriteLine("\t" + osoba.Wiek);
}
Przydatne jest to też wtedy, gdy nie chcesz zapoznawać, z jakim typem masz do czynienia.
Kopiowanie Tablic
Pamiętaj , że tablicę są typami referencyjnymi i są tak naprawdę instancjami klasy System.Array.
Czyli przyrównanie jednej tablicy do drugiej spowoduje tylko przepisanie referencji. Dwie tablicę będą referować się do tego samego zbioru danych na stercie.
Metodą kopiowania wartości z jednej tablic do drugiej może być zwykła pętla for.
int[] numery = new int[3] { 9, 8, 7 };
int[] copy = new int[numery.Length];
for (int i = 0; i < numery.Length; i++)
{
copy[i] = numery[i];
}
Jednak taki zapis wydaje się niepotrzebnie skomplikowany. Kopiowanie tablic to dość częsty zabieg w programowaniu. Nic dziwnego , że mamy do dyspozycji metodę, która potrafi skopiować zawartość tablicy z jednej do drugiej.
Jest to metoda “CopyTo()”. W parametrach tej metody musisz podać tablicę, z której mają być skopiowane wartości oraz numer indeksu, od którego proces kopiowania ma się zacząć.
int[] numery = new int[3] { 9, 8, 7 };
int[] copy = new int[numery.Length];
copy.CopyTo(numery, 0);
Innym sposobem kopiowania tablic jest metoda statyczna Copy z klasy System.Array.
Ma ona kilka wersji możemy w niej np. skopiować wartości od danego indeksu z kopiowanej tablicy do danego indeksu z docelowej tablicy. Przy czym możemy jeszcze podać długość i ilość elementów do kopiowania.
Oto przykład użycia metody Array.Copy()
int[] numery = new int[3] { 9, 8, 7 };
int[] copy = new int[6];
Array.Copy(numery, 0, copy, 3, 3);
A oto rezultat kodu.
Skopiowałem 3 elementy z tablicy numery i wykleiłem wartości do tablicy kopii, zaczynając od indeksu 3.
Ostatnią możliwością kopiowania elementów z jednej tablic do innej jest metoda Clone(). Ta metoda jest użyteczna bardziej dla klas.
int[] numery = new int[3] { 9, 8, 7 };
int[] copy = (int[])numery.Clone();
Metoda Clone zwraca obiekt, więc musi zostać wykonane rzutowanie. W tym wypadku naprawdę sklonowaliśmy tablicę.
Używanie tablic wielowymiarowych.
Najczęściej w programowaniu spotkasz się z tablicą jednowymiarową. W takim wypadku może o niej pomyśleć jak o liście elementów , a nie tablicy.
Tworząc tablicę dwuwymiarową musisz ustalić dwa indeksy. Jej wygląd będzie przypomniał kwadrat. Analogicznie tablicę trójwymiarową może obrazować sześcian , a czterowymiarową hipersześcian xD. Dzięki takim wizualizacjom łatwiej jest zrozumieć jak tablice wielowymiarowe działają.
Następujący kod tworzy tablicę dwuwymiarową z 80 elementami. Zwróć uwagę na przecinek w nawiasie.
int[,] elementy = new int[10, 8];
Aby mieć dostęp do wybranego elementu w tablicy 2D, musisz podać 2 indeksy oznaczające symbolicznie w tym przypadku numer wiersza i numer kolumny .
int[,] elementy = new int[10, 8];
elementy[0, 0] = 121;
elementy[9, 7] = 999;
elementy[5, 5] = elementy[0, 0];
elementy[5, 6]++;
elementy[5, 7] = elementy[9, 7]++;
elementy[5, 7] -= 199;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 8; j++)
{
elementy[i, j] = elementy[5, 7]+
elementy[0,0] + elementy[9,7]
- elementy[5,6] - elementy[5,5];
}
}
Jak widzisz w tablicach wielowymiarowych już nie możesz stosować właściwości lenght ponieważ podaje ona całkowitą wielkość tablicy. Do przeglądania tablic dwuwymiarowych dobrze sprawuje się pętla for dwa razy.
Ale jeszcze lepiej działa pętla foreach z tym samymi zaletami i wadami co wcześniej. Przejdzie ona po całej tablicy od [0,0] ,[0,5]… do końca.
int aaa;
foreach (var item in elementy)
{
aaa = item;
}
Nie ma żadnych ograniczeń co do tworzenia wielowymiarowych tablic poza twoją pamięcią w komputerze. W wypadku tworzenia dużych tablic wielowymiarowych trzeba się spodziewać wyjątku “OutOfMemoryException”.
Każdy następny wymiar pomnaża ilość wartości w tablicy.
Co dalej:
W następnym wpisie omówię klasy z przestrzeni nazw “System.Collection”.