CodeGym /Kursy /JAVA 25 SELF /Podstawowe operacje na tablicach jednowymiarowych

Podstawowe operacje na tablicach jednowymiarowych

JAVA 25 SELF
Poziom 7 , Lekcja 2
Dostępny

1. Tablica typu String

Krótko opowiemy o tablicy typu String.

Jak już mówiliśmy, tablica może być dowolnego typu. To znaczy, że można utworzyć tablicę typu String. Oto jak wyglądałby kod, gdybyśmy mieli napisać program, który „wczytuje z klawiatury 10 wierszy i wypisuje je na ekran w odwrotnej kolejności”.

String[] array = new String[10];	// Tworzymy obiekt–tablicę na 10 elementów
for (int i = 0; i < 10; i++)		// Pętla od 0 do 9
{
   array[i] = console.nextLine();	// Czytamy wiersz z klawiatury i zapisujemy go do komórki tablicy
}
for (int i = 9; i >= 0; i--)		// Pętla od 9 do 0
{
    System.out.println(array[i]);	// Wypisujemy na ekran kolejną komórkę tablicy
}

Kod praktycznie się nie zmienił! Trzeba było tylko przy tworzeniu tablicy zamienić typ int na String. Poza tym wszystko jest dokładnie takie samo!

Wprowadzenie do null

Zagadka — co znajduje się w komórkach każdej z tych tablic tuż po utworzeniu?

int[] numbers = new int[10];
String[] strings = new String[10];
User[] users = new User[10];

Jeśli się nad tym zastanowić, to najprawdopodobniej komórki numbers zawierają 0. Zgadza się.

W takim razie, zgodnie z tą samą logiką, komórki strings powinny być wypełnione pustymi łańcuchami — "". Powiedzmy.

A czym wypełnione są komórki tablicy users? Pustymi obiektami User?

I tu tkwi haczyk: nie dla każdego typu danych istnieje „wartość domyślna”. Dlatego twórcy Javy wymyślili specjalną stałą — null. null to pusta referencja. Gdy tworzona jest zmienna typu obiektowego, jej wartość początkowa to null: referencja do niczego, brak referencji.

String name; 	// name zawiera null
name = "Alex";  // name zawiera referencję do obiektu/łańcucha "Alex"
name = null; 	// name zawiera null

Praca z null

Nie wolno wywoływać metod na obiekcie, jeśli referencja ma wartość null — obiektu przecież nie ma! W takim kodzie program zakończy się błędem:

String name; 	// name zawiera null
String upperName = name.toUpperCase(); // Wystąpi błąd NullPointerException: name == null

Jeśli chodzi o naszą „zagadkę”, odpowiedź jest taka:

int[] numbers = new int[10];		// {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
String[] strings = new String[10];  // {null, null, null, null, null, null, null, null, null, null}
User[] users = new User[10];		// {null, null, null, null, null, null, null, null, null, null}

Tylko typy prymitywne mają „wartość domyślną”. Wszystkie pozostałe typy domyślnie mają wartość null. Wartość początkowa String to nie jest pusty łańcuch. Takie życie.

2. Tablica typu String w pamięci

Rysunek 1. Jak obiekt String jest rozmieszczony w pamięci:

Tablica typu String w pamięci

Zwróć uwagę, że tekst łańcucha nie jest przechowywany bezpośrednio w zmiennej: dla niego przydzielany jest osobny blok pamięci. W zmiennej typu String przechowywany jest adres (referencja) do obiektu z tekstem.

Rysunek 2. Jak tablica liczb całkowitych jest rozmieszczona w pamięci:

Tablica int w pamięci

Ten obrazek także już widzieliście.

Rysunek 3. Jak w pamięci rozmieszczona jest tablica łańcuchów:

Jak w pamięci rozmieszczona jest tablica String

Po lewej widzimy zmienną–tablicę typu String[] (przechowuje adres obiektu–tablicy).

Pośrodku — obiekt–tablica typu String.

A po prawej — obiekty–łańcuchy, które przechowują jakieś teksty.

W komórkach obiektu–tablicy typu String nie są przechowywane same łańcuchy (teksty), lecz ich adresy (referencje). Dokładnie tak samo, jak w zmiennych typu String przechowywane są adresy łańcuchów (tekstu).

3. Szybka inicjalizacja tablicy w Javie

Tablice to bardzo przydatna rzecz, więc twórcy Javy postarali się maksymalnie ułatwić pracę z nimi. Pierwsze, co zrobili, to uproszczenie inicjalizacji tablicy, czyli nadawanie jej wartości początkowych.

Bardzo często, oprócz danych, które program skądś odczytuje, potrzebne są mu jeszcze wewnętrzne dane. Na przykład musimy przechowywać w tablicy długości wszystkich miesięcy. Jak może wyglądać ten kod:

int[] months = new int[12];
months[0] = 31; // styczeń
months[1] = 28; // luty
months[2] = 31; // marzec
months[3] = 30; // kwiecień
months[4] = 31; // maj
months[5] = 30; // czerwiec
months[6] = 31; // lipiec
months[7] = 31; // sierpień
months[8] = 30; // wrzesień
months[9] = 31; // październik
months[10] = 30; // listopad
months[11] = 31; // grudzień

Ale można to zapisać krócej — dzięki twórcom Javy:

// długości miesięcy roku
int[] months = new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

Wystarczy po prostu wyliczyć po przecinku wszystkie wartości tablicy!

Okazuje się, że kompilator potrafi określić typ kontenera (obiektu–tablicy) na podstawie typu zmiennej–tablicy. A aby określić długość tablicy — po prostu zlicza liczbę elementów zapisanych w nawiasach klamrowych.

Dlatego ten kod można zapisać jeszcze krócej:

// długości miesięcy roku
int[] months = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

Czy to nie jest piękne? 🙂 Taki zapis nazywa się „szybką inicjalizacją tablicy”. Działa, swoją drogą, nie tylko dla typu int...

// nazwy miesięcy roku
String[] months = { "Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec", "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień" };

4. Pętla for-each

Przejście po wszystkich elementach tablicy to tak częsta operacja, że wymyślono do tego specjalną pętlęfor-each. Zapisuje się ją tak:

for (int score : scores)
{
    System.out.println("Punkty: " + score);
}
Iteracja po elementach tablicy za pomocą for-each

Tutaj pętla sama iteruje po elementach — bez ręcznego indeksu. Nie można jednak zmieniać elementów bezpośrednio (w zmiennej pętli znajduje się tylko kopia wartości), za to bardzo wygodnie jest po prostu „przejść się” po tablicy.

Kompilator przekształci kod pętli for-each w poniższy kod:

for (int i = 0; i < scores.length; i++)
{
    int score = scores[i];
    System.out.println("Punkty: " + score);
}
Jak działa for-each „pod maską”

Żadnej magii. Teraz widać, dlaczego nie można zmienić elementów tablicy przez zmienną score.

Napiszmy jeszcze przykład: znajdziemy sumę wszystkich punktów.

int sum = 0;
for (int score : scores)
{
    sum += score;
}
System.out.println("Suma wszystkich punktów: " + sum);
Obliczanie sumy wartości tablicy za pomocą for-each

Kiedy używać której pętli?

  • Jeśli potrzebny jest indeks albo trzeba modyfikować elementy — pętla for.
  • Jeśli chcesz tylko przejść po wartościach — for-each jest szybsze i czytelniejsze.

5. Zmiana elementów tablicy

A co, jeśli musimy zwiększyć każdą ocenę o 1 punkt (na przykład za dobre zachowanie)? Tutaj potrzebna jest klasyczna pętla for z indeksem, bo tylko tak możemy zmienić wartości w tablicy.

Przykład: zwiększyć każdy element o 1

for (int i = 0; i < grades.length; i++) {
    grades[i] = grades[i] + 1;
}

Po wykonaniu tego kodu w tablicy grades będą nowe wartości.

Dlaczego nie można zrobić tego przez for-each?

for (int grade : grades) {
    grade = grade + 1; // Nie działa!
}

Ten kod nie zmieni tablicy, ponieważ grade — to kopia wartości z tablicy, a nie referencja do samego elementu.

6. Obliczenia na podstawie tablicy

Tablice często wykorzystuje się do obliczeń: suma, średnia, maksimum, minimum i tak dalej.

Przykład: suma wszystkich elementów tablicy

int sum = 0;
for (int i = 0; i < grades.length; i++) {
    sum += grades[i]; // to samo co sum = sum + grades[i];
}
System.out.println("Suma ocen: " + sum);

Przykład: wyszukiwanie wartości maksymalnej

int max = grades[0]; // zaczynamy od pierwszego elementu
for (int i = 1; i < grades.length; i++) {
    if (grades[i] > max) {
        max = grades[i];
    }
}
System.out.println("Maksymalna ocena: " + max);

Przykład: wyszukiwanie wartości minimalnej

int min = grades[0]; // zaczynamy od pierwszego elementu
for (int i = 1; i < grades.length; i++) {
    if (grades[i] < min) {
        min = grades[i];
    }
}
System.out.println("Minimalna ocena: " + min);

Przykład: obliczanie średniej arytmetycznej

int sum = 0;
for (int i = 0; i < grades.length; i++) {
    sum += grades[i];
}
double average = (double) sum / grades.length; // koniecznie rzutować na double!
System.out.println("Średnia ocena: " + average);

Zwróć uwagę: aby otrzymać liczbę niecałkowitą, trzeba jawnie rzutować sumę na typ double przed dzieleniem.

7. Zadania praktyczne

Wczytywanie tablicy z klawiatury

Często trzeba wypełnić tablicę wartościami wprowadzanymi przez użytkownika. Do tego używamy klasy Scanner i pętli.

Scanner console = new Scanner(System.in);

int n = 5; // rozmiar tablicy
int[] numbers = new int[n];

System.out.println("Wprowadź " + n + " liczb:");
for (int i = 0; i < n; i++) {
    numbers[i] = console.nextInt();
}

System.out.println("Wprowadzono:");
for (int i = 0; i < n; i++) {
    System.out.println(numbers[i]);
}

Wypisywanie tablicy w odwrotnej kolejności

Czasem trzeba wypisać elementy tablicy od końca (na przykład, aby sprawdzić, kto wszedł do pokoju jako ostatni).

for (int i = grades.length - 1; i >= 0; i--) {
    System.out.println("Ocena nr " + (i + 1) + ": " + grades[i]);
}

8. Typowe błędy przy pracy z tablicami jednowymiarowymi

Błąd nr 1: wyjście poza granice tablicy

Najczęstszy problem — próba odwołania się do nieistniejącego elementu. W Javie prowadzi to do wyjątku ArrayIndexOutOfBoundsException. Pamiętaj: ostatni dozwolony indeks — to arr.length - 1.

int[] arr = new int[5];
System.out.println(arr[5]); // Błąd! Indeksy od 0 do 4.

Błąd nr 2: zapomniano zainicjować tablicę

Zadeklarowano zmienną, ale nie utworzono tablicy:

int[] arr;
arr[0] = 5; // Błąd! Tablica nie została utworzona.

Należy koniecznie utworzyć tablicę za pomocą new:

arr = new int[10];

Błąd nr 3: próba zmiany elementów tablicy przez for-each

W pętli for-each zmienna — to kopia wartości, a nie referencja do elementu:

for (int x : arr) {
    x = 100; // Nie zmienia tablicy!
}

Aby zmienić wartości, użyj zwykłej pętli for z indeksem.

Błąd nr 4: nieprawidłowe użycie długości tablicy

Czasem myli się długość tablicy z ostatnim indeksem:

for (int i = 0; i <= arr.length; i++) { // Błąd! Powinno być i < arr.length
    // ...
}

Taki kod spowoduje wyjście poza granice.

Błąd nr 5: niejawna konwersja typów

Jeśli tablica jest typu int, nie można bezpośrednio przypisać jej wartości typu double:

int[] arr = new int[3];
arr[0] = 3.14; // Błąd! 3.14 to double.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION