CodeGym /Kursy /C# SELF /Tablice zębate (Jagged Arrays) w C#

Tablice zębate (Jagged Arrays) w C#

C# SELF
Poziom 7 , Lekcja 5
Dostępny

1. Czym tablice zębate różnią się od dwuwymiarowych

No i dotarliśmy do tematu, który wielu nazywa „tablicami tablic” albo „tablicami zębatymi” — po angielsku jagged arrays. W przeciwieństwie do tablic dwuwymiarowych, tablice zębate pozwalają przechowywać kolumny o różnej długości. To jakbyś miał kompleks budynków, gdzie każdy budynek ma inną liczbę mieszkań — w jednym 5, w drugim 20, a w trzecim tylko jedno.

Tablica zębata to tablica, której każdy element sam jest tablicą. Przy czym te 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 tyle samo elementów. Przykład: int[,] grid = new int[3, 5]; — zawsze mamy 3 linie po 5 elementów.
  • W tablicy zębatej każda linia może mieć inną długość! Przykład: int[][] jagged = new int[3][]; — a potem każdą linię (podtablicę) inicjalizujemy osobno.

Tak to wygląda wizualnie:

Tablica dwuwymiarowa Tablica zębata
Liczba elementów Ściśle ustalona (np. 3x5) Może się różnić między liniami
Indeksowanie
[i, j]
[i][j]
Elastyczność Niska Wysoka
Zastosowanie Tabele, matematyka Nierówne dane:
listy studentów z różną liczbą ocen, trójkąty

Wizualizacja: porównanie tablicy dwuwymiarowej i zębatej

Tablica dwuwymiarowa (3x3):
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ 9 │
└───┴───┴───┘

Tablica zębata (różne długości):
┌───┬───┐
│ 1 │ 2 │
├───┼───┼───┬───┐
│ 3 │ 4 │ 5 │ 6 │
├───┼───┴───┴───┘
│ 7 │
└───┘

2. Składnia deklaracji i inicjalizacji tablicy zębatej

Deklaracja tablicy zębatej nie jest straszniejsza niż poprzednich typów! Nie bój się podwójnych nawiasów kwadratowych:

int[][] jaggedArray = new int[3][];
Deklaracja tablicy zębatej

To znaczy, że mamy tablicę z 3 elementami i każdy z nich to też tablica int-ów. Ale na razie te wewnętrzne tablice nie istnieją! Żeby to lepiej ogarnąć, rozbijmy to na kroki.

Krok po kroku: inicjalizacja tablicy zębatej

Krok 1 — tworzenie głównej (zewnętrznej) tablicy:

int[][] jaggedArray = new int[3][];

Teraz mamy 3 „linie”, ale wszystkie są na razie równe null.

Krok 2 — tworzenie i wypełnianie wewnętrznych tablic (podtablic):

Załóżmy, że pierwsza linia ma długość 2, druga — 4, trzecia — 3:

jaggedArray[0] = new int[2]; // 2 elementy w pierwszej linii
jaggedArray[1] = new int[4]; // 4 elementy w drugiej linii
jaggedArray[2] = new int[3]; // 3 elementy w trzeciej linii

Krok 3 — wypełnianie wartościami:

Wewnętrzne tablice 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 zębatej

Możesz od razu stworzyć i wypełnić tablicę zębatą, 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 wewnętrznych tablic:

int[][] jaggedArray = {
    new[] { 1, 2 },
    new[] { 3, 4, 5, 6 },
    new[] { 7, 8, 9 }
};

3. Iteracja i praca z tablicami zębatymi

Iterowanie po tablicy zębatej nie jest trudniejsze niż po dwuwymiarowej, ale teraz zewnętrzna pętla idzie po liniach, a wewnętrzna po elementach linii (które mogą mieć różną długość):

for (int i = 0; i < jaggedArray.Length; i++)
{
    Console.WriteLine($"Linia {i}:");
    for (int j = 0; j < jaggedArray[i].Length; j++)
    {
        Console.Write($"{jaggedArray[i][j]} ");
    }
    Console.WriteLine();
}

Efekt na ekranie:

Linia 0:
1 2 
Linia 1:
3 4 5 6 
Linia 2:
7 8 9 

Możesz użyć foreach, żeby nie myśleć o indeksach:

foreach (int[] row in jaggedArray)
{
    foreach (int value in row)
    {
        Console.Write($"{value} ");
    }
    Console.WriteLine();
}

4. Jak działa tablica tablic

A teraz zobaczysz, jak naprawdę działają tablice tablic. Gotowy?

Jeśli w przypadku zwykłej tablicy „zmienna-tablica przechowuje referencję do kontenera, który trzyma elementy tablicy”. To w przypadku tablic zębatych sytuacja jest trochę bardziej wybuchowa: zmienna-tablica-tablic przechowuje referencję do kontenera, który trzyma referencje do tablic jednowymiarowych. Lepiej to zobaczyć niż sto razy tłumaczyć:

How two-dimensional arrays work

Po lewej mamy „zmienną-tablicę-tablic”, która trzyma referencję do „obiektu-kontenera tablic”. W środku mamy „obiekt-kontener tablic”, w którego komórkach są referencje do tablic jednowymiarowych — linii tablicy zębatej. No i po prawej widzisz cztery tablice jednowymiarowe — linie naszej tablicy zębatej.

Tak właśnie naprawdę działają tablice zębate. I takie podejście daje C#-programiście kilka plusów:

Po pierwsze, ponieważ „kontener kontenerów” trzyma referencje do „tablic-linii”, możemy bardzo szybko i łatwo zamieniać linie miejscami. Żeby dostać się do „kontenera kontenerów”, wystarczy podać jeden indeks zamiast dwóch. Przykład:
int[][] data = new int[2][];
data[0] = new int[5]; // pierwsza linia — tablica 5 elementów
data[1] = new int[5]; // druga linia — tablica 5 elementów

int[] row1 = data[0];
int[] row2 = data[1];

Takim kodem możesz zamienić linie miejscami:

// Ważna macierz z danymi
int[][] matrix = {
  new int[] {1, 2, 3, 4, 5},
  new int[] {5, 4, 3, 2, 1}
};

int[] tmp = matrix[0];
matrix[0] = matrix[1];
matrix[1] = tmp;

Jeśli odwołujesz się do komórki tablicy dwuwymiarowej, ale po nazwie tablicy podajesz tylko jeden indeks, to dostajesz się do kontenera kontenerów, w którego komórkach są referencje do zwykłych tablic jednowymiarowych.

5. Typowe scenariusze użycia tablic zębatych

Kiedy tablica zębata może być lepsza niż dwuwymiarowa?

  • Gdy przechowujesz dla każdego użytkownika inną liczbę jakichś danych: oceny z przedmiotów, zakupy, komentarze itd.
  • Gdy twoje dane mają trójkątną albo schodkową strukturę (np. do rysowania piramidek, trójkątów Pascala itd.).
  • Gdy chcesz oszczędzać pamięć: w tablicy dwuwymiarowej wszystkie linie są stałe, a w zębatej — tylko tyle elementów, ile trzeba.

Przykład z życia: menedżer ocen studentów

Rozwińmy nasz projekt! Załóżmy, że każdy student może mieć inną liczbę ocen z każdego przedmiotu. Jeden robi więcej zadań, inny mniej. Do tego idealnie nada się tablica zębata.

Załóżmy, że mamy trzech studentów i oto ich oceny z różnych zadań 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

Wyświetlmy oceny każdego studenta:

for (int i = 0; i < studentMarks.Length; i++)
{
    Console.Write($"Student {i}: ");
    for (int j = 0; j < studentMarks[i].Length; j++)
    {
        Console.Write(studentMarks[i][j] + " ");
    }
    Console.WriteLine();
}

Tablice zębate z innymi typami

Tablica zębata może być tablicą czegokolwiek: stringów, tablic innych tablic (głębiej!), nawet twoich własnych obiektów.

Przykład: tablica stringów

string[][] groups = new string[][]
{
    new string[] { "Ivan", "Piotr" },
    new string[] { "Maria", "Aleksej", "Sergiej" },
    new string[] { "Wasilisa" }
};

6. Cechy i możliwe błędy

Tablice zębate są elastyczne, ale pułapki czają się na każdym kroku.

  • Jeśli nie zainicjalizujesz którejś z wewnętrznych tablic (jaggedArray[1] = ...), próba dostępu do niej skończy się NullReferenceException. Nie zapomnij zainicjalizować każdej wewnętrznej tablicy!
  • Nie wszystkie linie (podtablice) mają tę samą długość. Jeśli użyjesz stałego indeksu w drugim wymiarze, możesz wyjść poza zakres.
  • Nie myl z tablicą dwuwymiarową! Indeksowanie wygląda tak: array[i][j], a nie array[i, j].
2
Zadanie
C# SELF, poziom 7, lekcja 5
Niedostępne
Deklaracja i inicjalizacja prostego tablicy postrzępionej
Deklaracja i inicjalizacja prostego tablicy postrzępionej
2
Zadanie
C# SELF, poziom 7, lekcja 5
Niedostępne
Największa wartość w tablicy nieregularnej
Największa wartość w tablicy nieregularnej
1
Ankieta/quiz
Tablice, poziom 7, lekcja 5
Niedostępny
Tablice
Wprowadzenie do tablic
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION