Twitter: @grzegg
Kategoria: java, Tagi: - - .

Java [17] – Tablice cz. III: tablice wielowymiarowe

Dotychczas omawialiśmy tablice jednowymiarowe, przyszedł jednak czas na wizytę w wyższych wymiarach ;-)

Ta strona jest częścią materiałów do kursu “Programowanie w Javie z elementami bioinformatyki dla poczatkujących”. Pozostałe materiały znajdziesz tutaj

Tablice dwuwymiarowe

O ile tablicę jednowymiarową możemy sobie wyobrazić jako półkę z ponumerowanymi przegródkami, to tablicę dwuwymiarową można przedstawić jako regał z ponumerowanymi półkami na których znajdują się ponumerowane przegródki. Jeśli chcielibyśmy określić położenie, dajmy na to, książki znajdującej się w konkretnej przegródce należałoby podać dwie liczby: numer półki i numer przegródki. Dwie liczby określają dwa wymiary.

Tworzenie tablic

Tablicę dwuwymiarową można zadeklarować na przykład w ten sposób:

int[][] tablica2D

a następnie zainicjalizować:


tablica2D = new int[10][10]

Oczywiście obie operacje można połączyć:


int[][] tablica2D = new int[10][10]

W ten sposób otrzymujemy tablicę, która ma formę „prostokątną” – każdy rząd ma tyle samo kolumn.


Liczba rzędów i kolumn nie musi być taka sama:


int [][] tablica= new int[6][10]


Jak widać w tym przypadku stworzyliśmy prostokątną tablicę, która ma 6 rzędów i 10 kolumn. Oczywiście to co jest rzędem a co kolumną jest sprawą umowną, tak naprawdę w pamięci komputera nie ma żadnych rzędów i kolumn (ani tym bardziej półek i przegródek). Niemniej kolejność liczb podanych w nawiasach ma znaczenie, ponieważ określa liczbę możliwych do zapamiętania elementów w każdym z wymiarów. Tablica dwuwymiarowa jest tak naprawdę tablicą, która przechowuje tablice jednowymiarowe jako elementy. Liczba podana jako pierwsza przy tworzeniu tablicy określa liczbę tablic jednowymiarowych, które mogą być przechowywane.

Wprowadzanie i pobieranie elementów w tablicach

Praca z tablicami dwuwymiarowymi w zasadzie przebiega podobnie jak z tablicami jednowymiarowymi, z tym oczywistym zastrzeżeniem, że odwołując się do konkretnej komórki tablicy należy podać dwie liczby, zamiast jednej. Na przykład, jeśli chcemy wprowadzić wartość do komórki znajdującej się w pierwszym rzędzie i drugiej kolumnie, można to zrobić tak:


tablica[0][1] = 4;

Czyli pierwsza liczba będzie oznaczała rząd a druga kolumnę.

Podobnie jak w przypadku tablic wymiarowych można od razu tablicę wymiarową wypełnić wartościami:


int[][] tablica2 = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};

Choć zdecydowanie bardziej czytelna będzie taka forma:


int[][] tablica2 = {{1,2,3,4},
{5,6,7,8},
{9,10,11,12}};

Teraz od razu widać, które dane mieszczą się w kolejnych rzędach, na kolejnych pozycjach.

Sposób pobierania poszczególnych danych, znów nie powinien być zaskoczeniem:


System.out.println("tablica2[1][3]: "+tablica2[1][3]);

Powyższy kod wyświetli zawartość czwartej komórki w drugim rzędzie.

Tablice nieprostokątne

Wyobrażanie sobie tablicy jako tabeli z rzędami i równą liczbą kolumn może być nieco zwodnicze w niektórych przypadkach, ponieważ liczba komórek w każdym z rzędów wcale nie musi być taka sama. Spróbuj wykonać taki kod:


int[][] tablica3 = {{1,2,3,4},
{5,6,7},
{9,10,11,12}};

System.out.println("tablica3[1][3]: "+tablica3[1][3]);

Tym razem otrzymujemy komunikat:


Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3


który jasno (tak, choć na początku to nie musi być oczywiste, ale przeczytaj go uważnie) informuje nas, że próbowaliśmy odwołać się do nieistniejącego elementu tablicy, inaczej przekroczyliśmy zakres indeksów komórek tablicy. W drugim rzędzie znajduje się tablica o tylko trzech elementach, próba odwołania się do komórki czwartej kończy więc niepowodzeniem.

Powyższy przypadek ukazuje naturę tablic dwuwymiarowych, a więc to, że jest to zbiór tablic jednowymiarowych, które znajdują się w strukturze wyższego rzędu.

Taka budowa tablic pozwala na stopniowe tworzenie tablic, to znaczy na definiowanie najpierw wyższego wymiaru a później w miarę potrzeb, dopiero niższego. Czyli najpierw można określić, ze tablica będzie przechowywała konkretną liczbę tablic jednowymiarowych a dopiero później określać jakiej długości to będą tablice. Na przykład może to wyglądać tak:

Pamiętaj jednak, że trzeba zachować odpowiednią kolejność w inicjalizacji tablic wielowymiarowych, od lewej do prawej. Nie można napisać na przykład tak:


int[][] tablica = new int[][3];

Tablice dwuwymiarowe i pętle

Pętle współdziałają z tablicami dwuwymiarowymi podobnie jak z jednowymiarowymi, oczywiście biorąc pod uwagę ich dwuwymiarowy charakter i strukturę (tablica jednowymiarowych tablic).

Na początek stwórzmy zmodyfikowaną wersję zadania z pierwszej części dotyczącej tablic. Wtedy tworzyliśmy jednowymiarową tablicę o 100 elementach, która najpierw była wypełniana liczbami od 0 do 10 a później jej zawartość była drukowana po 10 liczb w rzędzie, przy czym liczby jednocyfrowe miały dodawane z przodu 0 (w ten sposób wszystkie liczby miały postać dwucyfrową). Przedstawienie 100 liczb w 10 rzędach po 10 liczb odpowiada 100-elementowej tablicy „kwadratowej” o 10 kolumnach i 10 rzędach. Aby wypełnij ją liczbami a później uzyskać jej zawartość, będziemy posługiwać się dwoma zagnieżdżonymi pętlami.

Zacznijmy od stworzenia tablicy i wypełnienia jej liczbami.

Jak wynika z komentarzy przy kodzie, pętla zewnętrzna odpowiedzialna jest za generowanie kolejnych indeksów odpowiadających rzędom, można więc powiedzieć, że „pracuje na rzędach”. Natomiast pętla wewnętrzna jest odpowiedzialna za kolumny, pozwala więc dotrzeć do kolejnych komórek. Mówiąc bardziej ściśle, pętla zewnętrzna wskazuje na kolejne tablice jednowymiarowe a pętla wewnętrzna na elementy w obrębie tablic jednowymiarowych.

Przy drukowaniu zawartości tablicy moglibyśmy wykorzystać identyczne pętle jak wyżej. Jednak tym razem zastosujemy pętle foreach co pozwoli przy okazji znów zilustrować naturę tablicy dwuwymiarowej:

Jak, mam nadzieję, pamiętasz z poprzednich lekcji, pętla foreach pobiera po kolei elementy z tablicy i przekazuje je do zmiennej. Zauważ, że pętla zewnętrzna w powyższym przykładzie pobiera tablice jednowymiarowe. Dopiero pętla wewnętrzna uzyskuje dostęp do komórek tworzących tablice jednowymiarowe.

Trzeci wymiar, czwarty wymiar…

Jeśli tablicę dwuwymiarową wyobrażaliśmy sobie jako regał z półkami to tablice trójwymiarową można przedstawić jako rząd ponumerowanych regałów z półkami, tablice czterowymiarową jako wiele ponumerowanych rzędów regałów z półkami itd… Jak widać każdy dodatkowy wymiar tablicy można rozumieć jako kolekcję tablic niższego wymiaru.

Deklaracja i tworzenie tablicy przebiega podobnie jak w przypadku tablic, które już poznaliśmy, z tym, że każdy dodatkowy wymiar jest oznaczany dodatkową parą nawiasów kwadratowych. Tak na przykład może wyglądać program wypełniający kolejnymi liczbami tablicę trójwymiarową:

Pętle odpowiedzialne za wypełnianie tablicy można napisać w inny, bardzie odporny na błędy sposób wykorzystując możliwość bezpośredniego pobrania informacji o długości tablicy, poznaną w pierwszej części poświęconej tablicom (tablica.length).

Ten sposób wykorzystuje hierarchiczną budowę tablic wielowymiarowych i ma jeszcze tą zaletę, że umożliwia obsługę tablic wielowymiarowych, nawet gdy poszczególne tablice składowe na danym poziomie różnią się wielkością.

Skoro tablica została wypełniona, czas wyświetlić jej zawartość Wykorzystamy do tego celu pętle foreach. Poszczególne tablice dwuwymiarowe wyświetlimy oddzielnie, przedzielając liniami z gwiazdkami, co pozwoli je oddzielić wizualnie od siebie.

Wynik powinien wyglądać tak:


00, 01, 02, 03, 04,
05, 06, 07, 08, 09,
10, 11, 12, 13, 14,
15, 16, 17, 18, 19,
*******************
20, 21, 22, 23, 24,
25, 26, 27, 28, 29,
30, 31, 32, 33, 34,
35, 36, 37, 38, 39,
*******************
40, 41, 42, 43, 44,
45, 46, 47, 48, 49,
50, 51, 52, 53, 54,
55, 56, 57, 58, 59,
*******************

Zadanie

Trójkąt Pascala

Poniżej znajduje się trójkąt Pascala o siedmiu rzędach.


Algorytm tworzenia trójkąta Pascala jest bardzo prosty. Pierwszy rząd składa się z jedynki, drugi z dwu jedynek ułożonych po bokach jedynki z pierwszego rzędu. Kolejne rzędy powstają w ten sposób, że po bokach stawia się jedynki a kolejne liczby stanowią sumę dwu sąsiednich liczb znajdujących się powyżej.


Napisz program, który utworzy trójkąt Pascala o zadanej przez użytkownika liczbie rzędów, umieszczając liczby w tablicy a następnie wydrukuje zawartość tablicy w ten sposób, że wszystkie liczby nieparzyste zostaną wydrukowane jako gwiazdki a puste miejsca i liczby parzyste jako spacje.

Ta strona jest częścią materiałów do kursu “Programowanie w Javie z elementami bioinformatyki dla poczatkujących”. Pozostałe materiały znajdziesz tutaj

10 komentarzy Java [17] – Tablice cz. III: tablice wielowymiarowe

  • javawatch

    System.out.println(„tablica2[1][3]: „+tablica2[1][3]);

    Powyższy kod wyświetli zawartość czwartej komórki w czwartym rzędzie.

    ?
    Czy jesteś pewien?

  • Grzegorz

    @javawatch
    Oczywiście był błąd. Poprawiłem.
    Dziękuję za zwrócenie uwagi!

  • Ania

    Rewelacyjny kurs! Bardzo bardzo bardzo dziękuje:-)
    W na prawdę przejrzysty i prosty sposób (trafiający do kompletnego laika) wyjaśniasz poszczególne zagadnienia!
    Na prawdę wielkie dzięki i pozdrowienia!

  • Joanna

    Dziękuję bardzo za świetny kurs:) Zapewnia szybką „przesiadkę” z C++ :D

  • Mateusz

    Coś z podstawą trójkąta jest nie tak, innymi słowy jest niemalże pusta.
    Jeżeli chodzi o sam kurs to ten trójkąt aktualnie to czarna magia :p

  • Karolina

    Hm… Mam problem z trójkątem. Znaczy niby wszystko ok, ale program nie wykonuje się. Wyświetla się tylko komunikat „Podaj ilość rzędów”, a potem działa w nieskończoność. Program nie wyświetla żadnych komunikatów o błędzie, jakichkolwiek danych.
    Kod wygląda tak:
    import java.util.Arrays;
    import java.util.Scanner;

    public class TablicaV {

    public static void main(String[] args){

    Scanner skaner = new Scanner(System.in);

    System.out.println(„Podaj ilość rzędów: „);
    int rzedy = skaner.nextInt();

    int kolumny = 2*rzedy + 1;

    int[][] tab = new int [rzedy][kolumny];

    tab [0][rzedy] = 1;

    for (int i = 1; i < rzedy; i++) {
    for (int j = rzedy – i; j <= rzedy + i; j++){
    tab [i][j] = tab[i-1][j=1]+tab[i-1][j+1];

    }
    }

    for(int[] tab1 : tab){
    System.out.println(Arrays.toString(tab1));
    }

    for (int [] tab1 : tab) {
    for (int x : tab1) {
    if(x%2 != 0 ) {
    System.out.print("*");
    } else {
    System.out.print(" ");
    }
    }

    System.out.println("");
    }
    }
    }
    Nie umiem znaleźć błędu :/ Proszę o jakąś podpowiedź :)

  • Slawek

    fajne…przyda sie w moich badaniach

Leave a Reply