1. Pojęcie tablicy. Realizacja tablic w Javie
Dane w programie mogą być organizowane w różny sposób. W szczególności
jako zestawy (powiązanych i/lub w okreslony sposób uporządkowanych) wartości.
W tym kontekście mówimy o strukturach danych.
Tablice są zestawami elementów (wartości) tego samego typu, ułożonych na określonych
pozycjach. Do każdego z tych elementów mamy bezpośredni (
swobodny - nie wymagający przeglądania innych elementów zestawu) dostęp
poprzez nazwę tablicy i pozycję elementu w zestawie, określaną przez
indeks lub indeksy tablicy.
Na przykład, tablica czterech liczb całkowitych może wyglądać tak. ![]()
Pierwszy element - liczba 21 ma indeks 0, drugi - liczba 13 indeks 1 itd. Deklaracja tablicy składa się z:
int[] arr; // jest deklaracją tablicy liczb całkowitych (typu int), String[] s; // jest deklarację tablicy referencji do obiektów klasy String Button[] b; // jest deklarację tablicy referencji do obiektów klasy Button double[][] d; // jest deklaracją dwuwymiarowej tablicy liczb rzeczywistych Ściślej można powiedzieć, że deklarowane są tu zmienne tablicowe. Typ takiej zmiennej jest typem referencyjnym, a jego nazwa składa się z nazwy typu elementów tablicy i nawiasów kwadratowych. W powyższych przykładach: zmienna arr jest typu int[] zmienna s jest typu String[] zmienna d jest typu double[][] Uwaga: rozmiar tablicy nie stanowi składnika deklaracji tablicy. Skoro tablice są obiektami - to jakich klas? Otóż w trakcie kompilacji
programu niejawnie tworzone są definicje klas dla tablic. Klasy te mają specjalne
nazwy - tylko dla potrzeb JVM (np. klasa opisująca jednowymiarową tablicę
liczb całkowitych ma nazwę [I) i jedno pole - stałą typu int o wartości równej
liczbie elementów tablicy
Jeżeli oswoimy się z myślą, że tablice są obiektami, to - przez analogię do innych obiektów - będzie nam łatwo zrozumieć różnicę pomiędzy deklaracją i utworzeniem tablicy.
Deklaracja tablicy tworzy referencję. Pamięć jest alokowana dynamicznie albo w wyniku inicjacji za pomocą nawiasów klamrowych albo w wyniku użycia wyrażenia new.
Inicjacja tablicy za pomocą nawiasów klamrowych może wystąpić wyłącznie w wierszu deklaracji tablicy i ma postać: { element_1, element_2, .... element_N } gdzie: element_i - i-ty element tablicy (wartość)
Np.
Drugi sposób utworzenia tablicy polega na zastosowaniu wyrażenia new. Tworzenie tablicy za pomocą wyrażenia new ma postać new T[n]; gdzie:
Uwaga: nawiasy są kwadratowe, a nie okrągłe, jak w przypadku użycia new z konstruktorem jakiejś klasy
Na przykład: int[] arr; // deklaracja tablicy arr = new int[4]; // utworzenie tablicy 4 elementów typu int Można to też zapisać od razu w wierszu deklaracji: int[] arr = new int[4]; Mechanizm działania jest tu identyczny jak w przypadku innych obiektów. Przypomina go poniższy rysunek. ![]() Zauważmy, że rozmiar tablicy może być ustalony dynamicznie, w fazie wykonania programu. Np.
Indeksy tablicy mogą być wyłącznie wartościami typu int.
Mogą być dowolnymi wyrażeniami, których wyliczenie daje wartość typu int. Tablice zawsze indeksowane są poczynając od 0. Czyli np. pierwszy element n-elementowej tablicy ma indeks 0, a ostatni - indeks n-1.
Ze względu na to, że wartości typu byte, char i short są w wyrażeniach "promowane"
(przekształcane) do typu int), to również wartości tych typów możemy używać
przy indeksowaniu tablic. Niedoposzczalne natomiast jest użycie wartości
typu long. Odwołanie do i-go elementu tablicy o nazwie tab ma postać:
tab[i] Ta konstrukcja składniowa traktowana jest jako zmienna, stanowi nazwę zmiennej - zatem możemy tej zmiennej przypisywać wartości innych wyrażeń oraz możemy używać jej wartości w innych wyrażeniach
Na przykład: public class Test { public static void main(String[] args) { int[] a = {1, 2, 3, 4 }; System.out.println(a[4]); System.out.println(a[3]); System.out.println(a[2]); System.out.println(a[1]); } }
Zauważmy - mamy tu tablicę składającą się z 4 liczb całkowitych. Chcemy po
kolei wyprowadzić jej elementy od ostatniego poczynając. Częstym błędem jest
zapominanie o tym, że tablice indeksowane są od zera: w tym programie
zapomniano o tym i próbowano odwołać się do ostatniego elementu tablicy a za pomocą a
[4] (ktoś pomyślał: skoro są cztery elementy - to ostatni jest a[4]). Tymczasem
jest to odwołanie poza zakres tablicy, do nieistniejącego 5-go elementu!
Ten błąd zostanie wykryty, na konsoli pojawi się komunikat i program zostanie
przerwany. Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
at Test.main(Test.java:5)
Proszę skompilować ten program, przekonać się jak działa w tej wersji, po czym:
W powyższym przykładzie było nieco żmudne wypisywanie kolejnych elementów
tablicy. W naturalny sposób powinniśmy to robić w pętli. public class Test { public static void main(String[] args) { int[] a = {1, 2, 3, 4 }; for (int i=3; i>=0; i--) System.out.println(a[i]); } }
Mhm, a co się stanie gdy zmienimy rozmiar tablicy dodając kilka nowych elementów
w inicjacji? Będziemy musieli od nowa policzyć elementy i zmienić inicjację
licznika pętli. Trochę niewygodne, a do tego naraża nas na błędy. A przecież
rozmiar tablicy znany jest JVM, niech zatem "liczeniem" elementów zajmuje
się komputer. Zawsze możemy uzyskać informacje o rozmiarze (liczbie elementów) tablicy za pomocą odwołania:
nazwa_tablicy.length Uwaga: częstym błędem jest traktowanie tego wyrażenia jako wywołania metody. W tym przypadku length nie jest nazwą metody (lecz pola niejawnie stworzonej klasy, opisującej tablicę), dlatego NIE STAWIAMY po nim nawiasów okrągłych Zatem poprzedni program można by zapisać tak: public class Test { public static void main(String[] args) { int[] a = {1, 2, 3, 4 }; for (int i=a.length-1; i>=0; i--) System.out.println(a[i]); } }
Spróbujmy teraz odwrócić kolejność wypisywania elementów tablicy (czyli po kolei od pierwszego poczynając). Przed lekturą dalszego tekstu proszę rozwiązać to zadanie samodzielnie
Rozwiązanie: public class Test { public static void main(String[] args) { int[] a = {1, 2, 3, 4 }; for (int i=0; i<a.length; i++) System.out.println(a[i]); } }
Przebięgając w pętli przez wszystkie (poczynając od pierwszego) elementy tablicy tab musimy zmieniać indeksy od 0 do tab.length-1, czyli zastosować następującą postać pętli for:
for (int i = 0; i < tab.length; i++) ... tab[i] ... ;
Użycie length wobec tablicy jest szczególnie wygodne w metodach, które otrzymują jako argumenty referencje do tablic: możemy w ten sposób pisać uniwersalne metody działające na tablicach o różnych rozmiarach.
|