Tablice - Java
Tablica to grupa zmiennych (nazywanych elementami lub komponentami) zawierająca wartości tego samego typu. Tablice są obiektami, więc są typami referencyjnymi. Jak się wkrótce przekonasz, to, co nazywamy potocznie tablicą, jest tak naprawdę referencją do obiektu tablicy. Elementami tablicy mogą być typy podstawowe lub typy referencyjne. Aby odnieść się do konkretnego elementu tablicy, podajemy nazwę referencji do tablicy i wartość położenia elementu w tablicy. Wartość
położenia nazywa się indeksem elementu.
Logiczna reprezentacja tablic
Rysunek 7.1 przedstawia logiczną reprezentację tablicy liczb całkowitych o nazwie c. Tablica składa się z 12 elementów. Program odnosi się do poszczególnych elementów za pomocą wyrażenia dostępu do tablicy, które zawiera nazwę tablicy i indeks elementu umieszczony w nawiasach kwadratowych ([]). Pierwszy element każdej tablicy ma indeks zero, więc często nazywa się go elementem zerowym. Elementami tablicy c są zatem c[0], c[1], c[2] itd. Najwyższym indeksem tablicy c jest 11, czyli o 1 mniej niż 12 (liczba elementów). Nazwy tablic wykorzystują te same konwencje nazewnicze co nazwy innych zmiennych. Indeks musi być liczbą całkowitą i nieujemną, która jest mniejsza od rozmiaru tablicy. Program może używać wyrażenia jako indeksu. Jeśli założymy, że zmienna a wynosi 5, natomiast zmienna b wynosi 6, to
c[a + b] += 2;
Rysunek 7.1. Tablica dwunastoelementowa
dodaje 2 do elementu c[11]. Indeksowana nazwa tablicy to wyrażenie dostępu do tablicy, które umieszczone po lewej stronie operatora przypisania, powoduje wstawienie nowej wartości do elementu tablicy.
Typowy błąd programistyczny
Indeksem musi być wartość typu int lub wartość, którą można promować do typu int, czyli byte, short lub char, ale nie long. W przeciwnym razie nastąpi błąd kompilacji.
Przyjrzyjmy się dokładniej tablicy c z rysunku 7.1. Nazwą tablicy jest c. Każdy obiekt tablicy zna swoją własną długość i przechowuje ją w zmiennej instancji o nazwie length. Wyrażenie c.length zwraca długość tablicy c. Choć zmienna instancji length jest publiczna, nie można jej zmienić, bo jest zadeklarowana jako final. Dwanaście elementów tablicy używa odnośników c[0], c[1], c[2], …, c[11].
Wartość c[0] wynosi -45, wartość c[1] to 6, wartość c[2] to 0, wartość c[7] to 62, a wartość c[11] to 78. Aby obliczyć sumę wartości znajdujących się pierwszych trzech elementach tablicy c i zapamiętać wynik w zmiennej sum, napisalibyśmy:
sum = c[0] + c[1] + c[2];
Aby podzielić wartość c[6] przez 2 i przypisać wynik do zmiennej x, napisalibyśmy:
x = c[6] / 2;
Deklaracja i tworzenie tablic
Obiekty tablic zajmują miejsce w pamięci. Podobnie jak obiekty, także tablice tworzy się z użyciem słowa kluczowego new. Aby utworzyć obiekt tablicy, określ typ elementów tablicy i liczbę elementów jako część wyrażenia tworzenia tablicy, które wykorzystuje słowo kluczowe new. Takie wyrażenie zwraca referencję, którą można zapamiętać w zmiennej tablicowej. Poniższe wyrażenie tworzenia tablicy tworzy obiekt tablicy zawierający 12 elementów int i zapamiętuje go w zmiennej c:
int[] c = new int[12];
To wyrażenie może posłużyć do utworzenia tablicy z rysunku 7.1. Po utworzeniu tablicy każdy z jej elementów otrzymuje wartość domyślną: zero dla podstawowych typów numerycznych, false dla boolean i null dla referencji. Jak się wkrótce przekonasz, można w trakcie tworzenia tablicy wskazać wartości inne niż domyślne. Tworzenie tablicy można także przeprowadzić w dwóch krokach:
c = new int[12]; // Utworzenie tablicy; przypisanie do zmiennej tablicowej
W deklaracji nawiasy kwadratowe służą do wskazania, że c to zmienna, która będzie się odnosiła do tablicy (zmienna będzie przechowywała referencję do tablicy). W instrukcji przypisania zmienna c otrzymuje referencję do nowej, dwunastoelementowej
tablicy typu int.
Typowy błąd programistyczny
Określenie liczby elementów tablicy w nawiasach kwadratowych deklaracji (czyli zapis int[12] c;) jest błędem składniowym.
Program może w jednej deklaracji tworzyć kilka tablic. Poniższy kod rezerwuje tablicę 100 elementów w b i tablicę 27 elementów w x:
Gdy typ tablicy i nawiasy kwadratowe łączy się na początku deklaracji, wszystkie identyfikatory w deklaracji dotyczą tablic. W tym przypadku zmienne b i x to tablice obiektów String. W celu zwiększenia czytelności warto stosować tylko jedną deklarację zmiennej w każdym wierszu. Oto zapis równoważny poprzedniemu:
String[] x = new String[27]; // Utworzenie tablicy x
Dobra praktyka programistyczna
W celu zwiększenia czytelności deklaruj tylko jedną zmienną w każdym wierszu. Dodatkowo umieszczaj również informację w komentarzu na temat przeznaczenia zmiennej.
Gdy w każdej deklaracji umieszcza się tylko jedną zmienną, nawiasy kwadratowe można umieścić po nazwie typu lub po nazwie zmiennej:
String x[] = new String[27]; // Utworzenie tablicy x
Zaleca się jednak stosowanie wersji, w której nawiasy znajdują się po typie.
Typowy błąd programistyczny
Deklaracja wielu zmiennych tablicowych w jednej instrukcji może prowadzić do subtelnych błędów. Weźmy za przykład deklarację int[] a, b, c;. Jeśli a, b i c mają być zmiennymi tablicowymi, taki zapis jest poprawny — umieszczenie nawiasów kwadratowych po typie oznacza, że wszystkie identyfikatory są tablicami. Gdybyśmy jednak chcieli, aby tylko pierwszy identyfikator dotyczył tablicy, a pozostałe zmiennych typu int, taki zapis byłby niepoprawny — prawidłowa byłaby wtedy wersja int a[], b, c;.
Program może deklarować tablice dowolnych typów. Każdy element tablicy typów podstawowych zawiera wartość tego elementu. W przypadku tablicy dotyczących referencji każdy element zawiera referencję do obiektu wskazanego typu. Każdy element tablicy int to wartość int, a każdy element tablicy String to referencja do obiektu String.
Przykłady użycia tablic
W tym podrozdziale przedstawimy kilka przykładów ilustrujących deklarowanie, tworzenie i inicjalizowanie tablic oraz modyfikowanie ich elementów.
Tworzenie i inicjalizacja tablicy
Aplikacja z rysunku 7.2 używa słowa kluczowego new do utworzenia tablicy zawierającej 10 elementów int, które są początkowe równe zero (domyślna wartość dla zmiennych typu int). Wiersz 7. deklaruje array — zmienną referencyjną umożliwiającą
przechowywanie tablicy elementów int — a następnie inicjalizuje ją obiektem tablicy z 10 elementami typu int. Wiersz 9. wyświetla nagłówki kolumn. Pierwsza kolumna zawiera indeks (od 0 do 9) elementów tablicy, a druga domyślną wartość początkową (0) w każdym elemencie tablicy.
2 // Inicjalizacja elementów tablicy na wartości domyślne wynoszące zero
3
4 public class InitArray {
5 public static void main(String[] args) {
6 // Deklaracja zmiennej tablicowej i inicjalizacja jej obiektem tablicy
7 int[] array = new int[10]; // Utworzenie obiektu tablicy
8
9 System.out.printf("%s%10s%n", "Indeks", "Wartość"); // Nagłówki kolumn
10
11 // Wyświetl wartości poszczególnych elementów tablicy
12 for (int counter = 0; counter < array.length; counter++) {
13 System.out.printf("%6d%10d%n", counter, array[counter]);
14 }
15 }
16 }
Indeks Wartość
0 0
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
9 0
Rysunek 7.2. Inicjalizacja elementów tablicy na wartości domyślne wynoszące zero
Instrukcja for (wiersze od 12. do 14.) wyświetla indeks (reprezentowany przez counter) i wartość każdego elementu tablicy (reprezentowaną przez array[counter]). Zmienna sterująca counter jest początkowo równa 0 — wartości indeksów zaczynają się od 0, czyli użycie licznika bazującego na zerze umożliwia pętli dostęp do każdego elementu tablicy. Warunek kontynuacji pętli używa wyrażenia array.length (wiersz 12.) do określenia długości tablicy. W tym przykładzie długość tablicy wynosi 10, więc pętla będzie wykonywana dopóty, dopóki zmienna sterująca counter będzie mniejsza niż 10. Najwyższy indeks tablicy dziesięcioelementowej
wynosi 9, więc zastosowanie operatora „mniejszy od” w warunku kontynuacji pętli gwarantuje, że pętla nie będzie próbowała korzystać z elementu spoza zakresu tablicy (w ostatniej iteracji counter wynosi 9). Wkrótce pokażemy, co się stanie, jeśli Java natknie się na indeks spoza zakresu w trakcie wykonywania programu.
Użycie inicjalizatora tablicy
Utworzenie tablicy i inicjalizacja jej elementów może odbyć się za pomocą inicjalizatora tablicy — listy oddzielonych przecinkami wyrażeń (nazywanej listą inicjalizacyjną) zamkniętej w nawiasach klamrowych. W tym przypadku długość tablicy określa liczba elementów z listy inicjalizacyjnej. Na przykład:
int[] n = {10, 20, 30, 40, 50};
tworzy pięcioelementową tablicę o indeksach od 0 do 4. Element n[0] jest zainicjalizowany wartością 10, n[1] wartością 20 itd. Gdy kompilator natknie się na deklarację tablicy, która zawiera listę inicjalizacyjną, zliczy on elementy listy i automatycznie utworzy tablicę o odpowiednim rozmiarze za pomocą operacji new. Aplikacja z rysunku 7.3 inicjalizuje tablicę liczb całkowitych dziesięcioma wartościami (wiersz 7.), a następnie prezentuje tablicę w formacie tabelarycznym. Kod wyświetlający elementy tablicy (wiersze od 12. do 14.) jest taki sam jak na rysunku 7.2 (wiersze od 12. do 14.).
2 // Inicjalizacja elementów tablicy na wartości z inicjalizatora tablicy
3
4 public class InitArray {
5 public static void main(String[] args) {
6 // Lista inicjalizacyjna określa wartości do umieszczenia w tablicy
7 int[] array = {32, 27, 64, 18, 95, 14, 90, 70, 60, 37};
8
9 System.out.printf("%s%10s%n", "Indeks", "Wartość"); // Nagłówki kolumn
10
11 // Wyświetl wartości poszczególnych elementów tablicy
12 for (int counter = 0; counter < array.length; counter++) {
13 System.out.printf("%6d%10d%n", counter, array[counter]);
14 }
15 }
16 }
Indeks Wartość
0 32
1 27
2 64
3 18
4 95
5 14
6 90
7 70
8 60
9 37
Rysunek 7.3. Inicjalizacja elementów tablicy na wartości z inicjalizatora tablicy
Obliczenie wartości elementów tablicy
Aplikacja z rysunku 7.4 tworzy dziesięcioelementową tablicę i przypisuje do kolejnych elementów liczby parzyste od 2 do 20 (2, 4, 6, …, 20). Następnie przedstawia wyniki w postaci tabelarycznej. Instrukcja for z wierszy od 10. do 12. oblicza elementy tablicy, mnożąc aktualną wartość sterującą przez 2 i dodając 2.
2 // Obliczenie wartości elementów tablicy
3
4 public class InitArray {
5 public static void main(String[] args) {
6 final int ARRAY_LENGTH = 10; // Deklaracja stałej
7 int[] array = new int[ARRAY_LENGTH]; // Utworzenie tablicy
8
9 // Obliczenie wartości dla każdego elementu tablicy
10 for (int counter = 0; counter < array.length; counter++) {
11 array[counter] = 2 + 2 * counter;
12 }
13
14 System.out.printf("%s%10s%n", "Indeks", "Wartość"); // Nagłówki kolumn
15
16 // Wyświetl wartości poszczególnych elementów tablicy
17 for (int counter = 0; counter < array.length; counter++) {
18 System.out.printf("%6d%10d%n", counter, array[counter]);
19 }
20 }
21 }
Indeks Wartość
0 2
1 4
2 6
3 8
4 10
5 12
6 14
7 16
8 18
9 20
Rysunek 7.4. Obliczenie wartości elementów tablicy
Wiersz 6. używa modyfikatora final do zadeklarowania stałej ARRAY_LENGTH o wartości 10. Zmienne finalne (stałe) trzeba zainicjalizować przed użyciem, a później nie można zmienić ich wartości. Próba zmiany zmiennej zadeklarowanej jako final spowoduje zgłoszenie błędu kompilacji typu:
Dobra praktyka programistyczna
Zmienne finalne nazywa się również stałymi nazwanymi. Dzięki nim programy stają się czytelniejsze — stała nazwana, taka jak ARRAY_LENGTH, wyraźnie wskazuje cel istnienia, a literał, taki jak 10, może mieć różne znaczenia w zależności od kontekstu.
Dobra praktyka programistyczna
Zgodnie z konwencją stałe powinny mieć nazwy pisane dużymi literami z zastosowaniem znaku podkreślenia (_) do oddzielania poszczególnych słów, np. ARRAY_LENGTH.
Typowy błąd programistyczny
Próba przypisania do zmiennej oznaczonej jako final kończy się błędem kompilacji. Podobnie próba użycia wartości takiej zmiennej przed jej zainicjalizowaniem skończy się błędem kompilacji „variable nazwaZmiennej might not have been initialized”.
Sumowanie elementów tablicy
Często elementy tablicy reprezentują wartości do użycia w obliczeniach. Jeśli wartościami są wyniki egzaminu, wykładowca może chcieć obliczyć sumę wartości i średnią ocen klasy. Technikę tę wykorzystują przykłady klasy GradeBook z rysunków 7.14 i 7.18.
Rysunek 7.5 przedstawia obliczenie sumy wartości umieszczonych w tablicy dziesięcioelementowej. Program deklaruje, tworzy i inicjalizuje tablicę w wierszu 6. Wiersze od 10. do 12. dokonują obliczeń.
2 // Obliczenie sumy elementów tablicy
3
4 public class SumArray {
5 public static void main(String[] args) {
6 int[] array = {87, 68, 94, 100, 83, 78, 85, 91, 76, 87};
7 int total = 0;
8
9 // Dodaj wartość elementu do sumy
10 for (int counter = 0; counter < array.length; counter++) {
11 total += array[counter];
12 }
13
14 System.out.printf("Suma elementów tablicy: %d%n", total);
15 }
16 }
Suma elementów tablicy: 849
Rysunek 7.5. Obliczenie sumy elementów tablicy
Oczywiście wartości, które znajdują się w inicjalizotorze tablicy, najczęściej wczytuje się do programu z innego źródła. Można na przykład poprosić o dane użytkownika lub wczytać je z pliku. Odczyt danych z zewnętrznego źródła (zamiast umieszczania ich „na sztywno” w kodzie źródłowym) czyni program bardziej użytecznym, ponieważ może być stosowany z różnymi zbiorami danych.
Programowanie w Javie. Solidna wiedza w praktyce. Wydanie XI, Autorzy: Paul Deitel, Harvey Deitel, Wydawnictwo: Helion