1. Tablice postrzępione różnią się od dwuwymiarowych
Doszliśmy do tematu, który wielu nazywa „tablicami postrzępionymi” — po angielsku jagged arrays. W odróżnieniu od tablic dwuwymiarowych, tablice postrzępione pozwalają przechowywać wiersze o różnej długości. To tak, jakbyś miał kompleks budynków, gdzie każdy budynek ma własną liczbę mieszkań — w jednym budynku jest 5 mieszkań, w innym 20, a w trzecim tylko jedno.
Tablica postrzępiona to tablica, której każdy element jest tablicą, przy czym wewnętrzne tablice (zwane też „podtablicami”) mogą mieć różną długość.
Główna różnica:
- W tablicy dwuwymiarowej każda „linia” (i każda „kolumna”) ma taką samą liczbę elementów. Przykład: int[][] grid = new int[3][5]; — mamy zawsze 3 wiersze po 5 elementów.
- W tablicy postrzępionej każdy wiersz może mieć inną długość! Przykład: int[][] jagged = new int[3][]; — a dopiero potem każdy wiersz (podtablicę) inicjalizujemy na swój sposób.
Tak to wygląda wizualnie:
Tablica dwuwymiarowa (3x3):
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ 9 │
└───┴───┴───┘
Tablica postrzępiona (różne długości):
┌───┬───┐
│ 1 │ 2 │
├───┼───┼───┬───┐
│ 3 │ 4 │ 5 │ 6 │
├───┼───┴───┴───┘
│ 7 │
└───┘
2. Składnia deklaracji i inicjalizacji tablicy postrzępionej
Deklaracja tablicy postrzępionej nie jest straszniejsza niż deklaracje wcześniejszych typów! Nie trzeba się bać podwójnych nawiasów kwadratowych:
int[][] jaggedArray = new int[3][];
To znaczy, że mamy tablicę z 3 elementów, z których każdy jest również tablicą typu int. Ale na razie tablice wewnętrzne nie zostały utworzone! Dla lepszego zrozumienia rozłóżmy to na kroki.
Inicjalizacja krok po kroku tablicy postrzępionej
Krok 1 — utworzenie głównej (zewnętrznej) tablicy:
int[][] jaggedArray = new int[3][];
Mamy teraz 3 „wiersze”, ale wszystkie mają wartość null.
Krok 2 — utworzenie i wypełnienie tablic wewnętrznych (podtablic):
Na przykład niech pierwszy wiersz ma długość 2, drugi — 4, trzeci — 3:
jaggedArray[0] = new int[2]; // 2 elementy w pierwszym wierszu
jaggedArray[1] = new int[4]; // 4 elementy w drugim wierszu
jaggedArray[2] = new int[3]; // 3 elementy w trzecim wierszu
Krok 3 — wypełnianie wartościami:
Tablice wewnętrzne to zwykłe tablice! Na przykład:
jaggedArray[0][0] = 1;
jaggedArray[0][1] = 2;
jaggedArray[1][0] = 3;
jaggedArray[1][1] = 4;
jaggedArray[1][2] = 5;
jaggedArray[1][3] = 6;
jaggedArray[2][0] = 7;
jaggedArray[2][1] = 8;
jaggedArray[2][2] = 9;
Skrócona inicjalizacja tablicy postrzępionej
Można utworzyć i wypełnić tablicę postrzępioną od razu, jeśli znasz wartości z góry:
int[][] jaggedArray = new int[][]
{
new int[] { 1, 2 },
new int[] { 3, 4, 5, 6 },
new int[] { 7, 8, 9 }
};
Albo jeszcze krócej, pomijając typ tablic wewnętrznych:
int[][] jaggedArray =
{
{ 1, 2 },
{ 3, 4, 5, 6 },
{ 7, 8, 9 }
};
3. Iteracja i praca z tablicami postrzępionymi
Iterować tablicę postrzępioną nie jest trudniej niż dwuwymiarową, ale teraz pętla zewnętrzna idzie po wierszach, a wewnętrzna — po elementach wiersza (które mogą mieć różną długość):
for (int i = 0; i < jaggedArray.length; i++)
{
System.out.println("Wiersz " + i + ":");
for (int j = 0; j < jaggedArray[i].length; j++)
{
System.out.print(jaggedArray[i][j] + " ");
}
System.out.println();
}
Wynik na ekranie:
Wiersz 0:
1 2
Wiersz 1:
3 4 5 6
Wiersz 2:
7 8 9
Można użyć for-each, aby nie myśleć o indeksach:
for (int[] row : jaggedArray)
{
for (int value : row)
{
System.out.print(value + " ");
}
System.out.println();
}
4. Typowe scenariusze zastosowań tablic postrzępionych
Kiedy tablica postrzępiona może być lepsza niż dwuwymiarowa?
- Gdy przechowujesz dla każdego użytkownika różną liczbę jakiś danych: oceny z przedmiotów, zakupy, komentarze itd.
- Gdy Twoje dane mają strukturę trójkątną lub schodkową (np. do wypisywania piramidek, trójkątów Pascala itp.).
- Gdy chcesz oszczędzać pamięć: w tablicy dwuwymiarowej wszystkie wiersze są stałe, a w postrzępionej — tylko tyle elementów, ile potrzeba.
Przykład z życia: menedżer ocen studentów
Załóżmy, że mamy trzech studentów, a oto ich oceny za różne zadania z matematyki:
| Student | Oceny |
|---|---|
| 0 | 5, 4 |
| 1 | 3, 4, 4 |
| 2 | 5 |
Zadeklarujmy taką tablicę:
int[][] studentMarks = new int[3][];
studentMarks[0] = new int[] { 5, 4 }; // Pierwszy student — 2 oceny
studentMarks[1] = new int[] { 3, 4, 4 }; // Drugi student — 3 oceny
studentMarks[2] = new int[] { 5 }; // Trzeci student — 1 ocena
Wypiszemy oceny każdego studenta:
for (int i = 0; i < studentMarks.length; i++)
{
System.out.print("Student " + i + ": ");
for (int j = 0; j < studentMarks[i].length; j++)
{
System.out.print(studentMarks[i][j] + " ");
}
System.out.println();
}
Użycie tablic postrzępionych z innymi typami
Postrzępiona może być tablica czegokolwiek: napisów, tablic innych tablic (głębiej!), nawet Twoich własnych obiektów.
Przykład: tablica napisów
String[][] groups = {
{ "Iwan", "Piotr" },
{ "Maria", "Aleksiej", "Siergiej" },
{ "Wasilisa" }
};
5. Trójwymiarowe i wielowymiarowe tablice
Jeszcze jedna ciekawostka o tablicach, której pewnie się domyślasz. Skoro można zrobić tablicę dwuwymiarową, to można też trójwymiarową?
Tak, można utworzyć tablicę dowolnej wymiarowości. Takie tablice nazywa się wielowymiarowymi.
Jak deklarować tablice wielowymiarowe
Wystarczy podać potrzebną liczbę rozmiarów w nawiasach:
int[][][] cube = new int[2][3][4]; // 2 "warstwy", 3 wiersze, 4 kolumny
cube[0][1][2] = 99;
Tu mamy trójwymiarową tablicę:
- 2 elementy względem pierwszej współrzędnej,
- 3 — względem drugiej,
- 4 — względem trzeciej.
Taka tablica to jeden duży „sześcian” danych, upakowany sekwencyjnie.
Iteracja po tablicy trójwymiarowej
Dostęp do elementu odbywa się przez wszystkie indeksy naraz:
for (int i = 0; i < cube.length; i++)
{
for (int j = 0; j < cube[i].length; j++)
{
for (int k = 0; k < cube[i][j].length; k++)
{
System.out.print(cube[i][j][k] + " ");
}
System.out.println();
}
System.out.println("---");
}
- Indeksy są numerowane od zera, jak zawsze w Java.
- Łącznie w takiej tablicy będzie 2 × 3 × 4 = 24 elementy.
Praktyczne przykłady tablic wielowymiarowych
- 2D — tabele, szachownice, obrazy.
- 3D — „klocki” w grafice komputerowej, dane do obliczeń naukowych (np. temperatura w różnych punktach przestrzeni i czasu).
- 4D i wyżej — rzadko używane, ale spotykane w zaawansowanej matematyce, symulacjach, uczeniu maszynowym itd.
6. Typowe błędy przy pracy z tablicami wielowymiarowymi
Błąd nr 1: Wyjście poza zakres tablicy
Najpopularniejszy błąd — próba odwołania się do nieistniejącego elementu, na przykład:
int[][] arr = new int[2][3];
arr[2][0] = 5; // Błąd! Nie ma wiersza o indeksie 2 (są tylko 0 i 1)
arr[0][3] = 7; // Błąd! Nie ma kolumny o indeksie 3 (są tylko 0, 1, 2)
Przy takim odwołaniu program wyrzuci ArrayIndexOutOfBoundsException. Zawsze sprawdzaj, że indeksy są w dozwolonych granicach: od 0 do length - 1.
Błąd nr 2: Niezinicjalizowane wiersze w tablicy postrzępionej
Jeśli utworzysz tablicę postrzępioną i zapomnisz zainicjalizować tablice wewnętrzne, przy próbie dostępu pojawi się NullPointerException:
int[][] jagged = new int[3][];
jagged[0][0] = 5; // Błąd! jagged[0] == null
Najpierw trzeba utworzyć tablicę wewnętrzną: jagged[0] = new int[2];
Błąd nr 3: Nieprawidłowe użycie długości tablicy
Myli się matrix.length (liczba wierszy) z matrix[0].length (liczba kolumn). Szczególnie często — przy kopiowaniu, iteracji, sumowaniu po kolumnach.
Błąd nr 4: Założenie, że wszystkie wiersze są tej samej długości
W tablicach postrzępionych wiersze mogą mieć różną długość! Jeśli piszesz matrix[i][j], upewnij się, że j < matrix[i].length.
Błąd nr 5: Pomieszanie kolejności indeksów
Czasem myli się, że najpierw idzie wiersz, a potem kolumna: matrix[wiersz][kolumna]. Nie na odwrót!
GO TO FULL VERSION