Podstawy programowania Podstawy C# Tablice
Tablica to indeksowany zbiór elementów Tablica jest typem referencyjnym (deklaracja tworzy tylko referencję, sama tablica musi być utworzona oddzielnie, najprościej operatorem new) Wszystkie elementy tablicy są tego samego typu Dostęp do elementów tablicy daje operator indeksowania [] (Ważne: indeks zaczyna się zawsze od wartości 0, a nie 1) Tablice mogą być jedno- lub wielowymiarowe Można tworzyć tablice tablic (jest to coś innego, niż tablica wielowymiarowa)
Deklaracja tablicy Można zadeklarować tablicę elementów dowolnego typu (wartościowego lub referencyjnego), dopisując "[]" po typie: Int32[] t1; // tablica liczb Int32 Button[] t2; // tablica referencji przycisków Użycie "[]" po nazwie typu deklaruje tablicę jednowymiarową, użycie "[, ]" dwuwymiarową, "[,, ]" trzywymiarową itd. Int32[, ] t3; // tablica dwuwymiarowa
Deklaracja tablicy Można połączyć deklarację tablicy z jej utworzeniem i nadaniem wartości wówczas rozmiar tablicy wynika z inicjalizatora. Inicjalizator tablicy to lista elementów, zapisana w nawiasach klamrowych i oddzielonych przecinkami: { p 0, p 1, p 2, p k-1 } Jeżeli tablica jest wielowymiarowa, to konstrukcja ta musi zostać zagnieżdżona, np. w tablicy trójwymiarowej każde p jest tablicą elementów q, a każde q jest tablicą elementów r: { p 0, p 1, p 2, p k-1 } {{q 0, q l-1 }, {q 0, q l-1 }, {q 0, q l-1 }} {{{r 0, r m-1 }, {r 0, r m-1 }}, {{r 0, r m-1 }, {r 0, r m-1 }}}
Deklaracja tablicy Przykłady: // tablica jednowymiarowa 4 el. Int32[] t1 = { 1, 2, 3, 4 }; // tablica dwuwymiarowa 2x3 el. Int32[,] t2 = { { 1, 2, 3 }, { 4, 5, 6 } }; // tablica trójwymiarowa 2x3x4 el. Int32[,,] t3 = { { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 0, 1, 2} } { {3, 4, 5, 6}, {7, 8, 9, 0}, {1, 2, 3, 4} } };
Utworzenie tablicy W celu utworzenia tablicy należy użyć operatora new, podając rozmiar tablicy: Int32[] t1; // deklaracja tablicy t1 = new Int32[5]; // utworzenie tablicy Deklarację i utworzenie tablicy można połączyć: Double[] t2 = new Double[3]; Int32[,] t3 = new Int32[3,5]; Można też połączyć utworzenie tablicy z nadaniem wartości; Liczba elementów musi być równa rozmiarowi Int32[] t4 = new Int32[3] {1, 2, 3}; // ok Int32[] t5 = new Int32[3] {1, 2}; // błąd!
Utworzenie tablicy Rozmiar tablicy może być stałą całkowitą, ale też zmienną lub dowolnym wyrażeniem o rezultacie całkowitym: Int32[] t1, t2, t3; Int32 r = 7; t1 = new Int32[7]; t2 = new Int32[r]; t3 = new Int32[2 * r + 1]; To ważne rozmiar nie musi być znany w czasie kompilacji, można utworzyć tablicę o rozmiarze zależnym od wykonania programu, np. podanym przez użytkownika
Wartości domyślne elementów Podczas tworzenia tablicy, jej wszystkie elementy są inicjalizowane tzw. wartością domyślną: 0 lub 0.0 dla typów prostych liczbowych, false dla typu Boolean '\0' dla znaków, "" (łańcuch pusty) dla łańcuchów null dla typów referencyjnych To inaczej, niż w C/C++, gdzie tablice nie są inicjalizowane, a po utworzeniu zawierają wartości przypadkowe (tj. śmieci pozostałe po poprzednio używanych zmiennych) To również inaczej, niż zwykłe zmienne w C#, które także nie są inicjowane
Wartości null Dotyczy wyłącznie typów referencyjnych i oznacza referencję pustą, tj. że obiekt wskazywany przez referencję nie istnieje. Wartość null ma istotne znaczenie praktyczne, np. pozwala sprawdzić, czy obiekt istnieje: obiect o = TryCreateObiect(); if (o!= null) // = jeżeli obiekt istnieje Przypisanie null do zmiennej referencyjnej jest dla CLR sygnałem, że obiekt wskazywany przez nią nie jest potrzebny, więc jego zasoby mogą zostać zwolnione przez GC: object o = new object(); // o = null; // zwolnienie zasobów obiektu przez GC
Indeksowanie Dostęp do elementów tablicy daje operator indeksowania []. Numery indeksów zaczynają się od 0, zatem tablica o rozmiarze N ma indeksy od 0 do N-1 Int32[] t = new Int32[3]; Int32 suma; t[0] = 7; // pierwszy element t[1] = 13; // drugi t[2] = 77; // trzeci i ostatni suma = t[0] + t[1] + t[2]; Próba odczytu lub przypisania elementu o indeksie spoza zakresu 0 N-1 spowoduje wyjątek programowy t[3] = 0; // błąd!
Indeksowanie Indeks może być stałą całkowitą, ale też zmienną lub dowolnym wyrażeniem o rezultacie całkowitym: Int32[] t = new Int32[100]; Int32 i = 7; t[i] = 7; t[i+1] = 13; t[7*i + 13] = 77; t[2*t[i] + 3] = 169; To ważne umożliwia przetwarzanie tablic przy pomocy pętli, oczywiście najczęściej jest to pętla for
Indeksowanie Do przetwarzania tablic prawie zawsze najlepszym wyborem jest pętla for ponieważ liczba elementów jest znana: Random r = new Random(); Int32 i; Int32[] t = new Int32[100]; for (i = 0; i < 100; i++) t[i] = r.next(); Dla liczb całkowitych równoważnie można napisać Int32[] t = new Int32[100]; for (i = 0; i <= 99; i++) jednak taki zapis zwiększa ryzyko błędu
Indeksowanie Do przetwarzania tablic prawie zawsze najlepszym wyborem jest pętla for ponieważ liczba elementów jest znana: Random r = new Random(); Int32 i; Int32[] t = new Int32[100]; for (i = 0; i < 100; i++) t[i] = r.next(); Każda iteracja pętli daje kolejną wartość indeksu tablicy, i = 0, 1, 2,, N-1 używając zapisu t[i] można wykorzystać zmieniany przez pętlę indeks do zapisania lub odczytania wartości, po jednej wartości w każdej iteracji
Indeksowanie Sposób konstrukcji pętli z warunkiem "licznik < rozmiar" jest jeszcze bardziej czytelny, gdy rozmiar jest zmienną: Random r = new Random(); Int32 i, N = 100; Int32[] t = new Int32[N]; for (i = 0; i < N; i++) t[i] = r.next(); Tym bardziej, że np. w algorytmach sortowania przegląda się tablicę od pierwszego do przedostatniego elementu, co w takim zapisie jest wyraźnie widoczne: for (i = 0; i < N - 1; i++) Równoważny zapis i <= N 2 sugeruje coś innego!
Tablice tablic Tablica może zawierać elementy dowolnego typu, zatem również inne tablice. Tablice tablic NIE są tablicami dwuwymiarowymi (nie można stosować ich zamiennie): Int32[,] t2w; // tablica dwuwymiarowa Int32[][] tt; // tablica tablic Tablice tablic wymagają dwuetapowego tworzenia: tt = new Int32[][2]; tt[0] = new Int32[5]; tt[1] = new Int32[10]; Tablice wchodzące w skład tablicy tablic mogą mieć różne rozmiary, dlatego takie tablice są nazywane jigged tables (tablice szczerbate)
Klasa Array Klasa Array jest tzw. klasą uniwersalną reprezentuje tablicę elementów dowolnego typu Jest to klasa abstrakcyjna nie można utworzyć obiektu Array Każda tablica, niezależnie od typu elementów składowych, należy do klasy Array (do zmiennej typu Array można przypisać dowolną tablicę), odwrotne przypisanie wymaga rzutowania Int32[] t = new Int32[3]; Array a; a = new Array(); // błąd! a = t; // ok t = a; // błąd! t = (Int32[])a; // ok
Klasa Array Klasa Array udostępnia wiele przydatnych właściwości i metod; Większość z nich to metody statyczne (wywoływane przez klasę Array, a nie obiekt tej klasy; pierwszym argumentem metody jest tablica) Int32[] t1 = { 1, 2, 3, 4 }; Int32[] t2; Int32 i; i = Array.IndexOf(t1, 3); // 2 Array.Reverse(t1); // {4,3,2,1} t2 = Array.FindAll(t1, n => n%2==0); // {4,2} Array.Sort(t2); // {2,4}
Klasa Array Klasa Array metody statyczne CreateInstance tworzy tablicę obiektów wskazanego typu Copy kopiuje fragment tablicy do innej tablicy Find, FindAll znajduje elementy spełniające określony warunek FindIndex znajduje element spełniający warunek i zwraca jego indeks IndexOf znajduje element o określonej wartości i zwraca jego indeks Resize zmienia rozmiar tablicy Reverse odwraca kolejność elementów Sort sortuje tablicę (jest kilkanaście wersji tej metody, można m.in. podać niestandardowy komparator)
Klasa Array Klasa Array właściwości i metody instancji (tj. dostępne przez zmienne klasy Array) metody: Clone tworzy tzw. płytką kopię tablicy CopyTo kopiuje fragment tablicy do innej tablicy GetLength podaje liczbę elementów określonego wymiaru właściwości Rank liczba wymiarów (liczona od zera!) Length łączna (dla wszystkich wymiarów) liczba elementów for (i=0; i<t.length; i++) Console.WriteLine(t[i]);
Klasa Enumerable Klasa statyczna, dostarcza kilkadziesiąt (!) statycznych metod przetwarzania dowolnych obiektów implementujących interfejs IEnumerable, tj. różne kolekcje (klasa Array też) Wszystkie metody są zdefiniowane jako tzw. rozszerzenia (extension), mogą być wywoływane na 2 sposoby: przez klasę Enumerable przez obiekt dowolnej klasy implementującej IEnumerable (znacznie wygodniej, można wykonać kilka operacji) Int32[] t1 = { 1, 2, 3, 4 }; Double avg; avg = Enumarable.Average(t1); avg = t1.average();
Klasa Enumerable Wiele metod daje jako rezultat kolekcję, jednak aby zapisać go jako tablicę, należy użyć metody ToArray Int32[] t1 = { 1, 2, 3, 4 }; Int32[] t2; t2 = t1.reverse().toarray(); // {4,3,2,1} Wywołania wielu kolejnych metod Enumerable można połączyć w łańcuch kolejna metoda łańcucha przetwarza rezultat dostarczony przez metodę poprzednią Int32[] t3 = { 2, 1, 2, 3, 2, 4 }; Int32[] t4; t4 = t3.distinct().where(n => n<4). Reverse().ToArray; // {3,1,2}
Klasa Enumerable, wybrane metody: All sprawdza czy wszystkie elementy spełniają wskazane kryterium Any sprawdza czy choć jeden elementy spełnia wskazane kryterium Average oblicza wartość średnią (elementy muszą być liczbami) Concat łączy dwie kolekcje Contains sprawdza czy kolekcja zawiera wskazany element Distinct zwraca kolekcję elementów unikalnych (bez powtórzeń) Except zwraca kolekcję za wyjątkiem wskazanych (różnica zbiorów) Max zwraca wartość maksymalną (elementy muszą być liczbami) Min zwraca wartość minimalną OrderBy sortuje według wybranego klucza i opcjonalnie komparatora Select zwraca kolekcję elementów przetworzonych p. zał. funkcję Sum oblicza sumę (elementy muszą być liczbami) Where zwraca podzbiór elementów spełniających zał. kryterium ToList przekształca kolekcję w listę ToArray przekształca kolekcję w tablicę