Helsingin yliopisto / Tietojenkäsittelytieteen laitos / 581258-1 Johdatus ohjelmointiin
Copyright © 1999 Arto Wikla. Tämän oppimateriaalin käyttö on sallittu vain yksityishenkilöille opiskelutarkoituksissa. Materiaalin käyttö muihin tarkoituksiin, kuten kaupallisilla tai muilla kursseilla, on kielletty.

2.8 Taulukko-olioista

(Muutettu viimeksi 18.10.2004)

Luku esittelee taulukoiden käytön perusideoita: määrittely, taulukosta etsiminen, taulukon järjestäminen. Taulukoiden alkioina käytetään alkeistyyppejä, eräitä olioita, mm. taulukoita.

Taulukon idea

Usein ohjelmassa joudutaan käsittelemään keskenään samatyyppisiä muuttujia "samaan tapaan". Tähän mennessä opituilla muuttujilla on jokaisella ollut aina oma erityinen nimensä ja siksi jokaisen muuttujan käsitteleminen on jouduttu ohjelmoimaan erikseen:

Olkoonpa ohjelmointitehtävänä tulostaa 100 syöttölukua käänteisessä järjestyksessä. Ainoa mahdollisuus ratkaista ongelma jo opitulla kalustolla on:

   int luku1, luku2, 
       .......           // nuo pisteet eivät ole Javaa!
       luku99, luku100;

   // luetaan 100 lukua:

   System.out.println("Anna luku 1");
   luku1 = Lue.kluku();
   System.out.println("Anna luku 2");
   luku2 = Lue.kluku();
       .......           // nuo pisteet eivät ole Javaa!

   System.out.println("Anna luku 100");
   luku100 = Lue.kluku();

   // tulostetaan luvut käänteisessä järjestyksessä:

   System.out.println("Luvut lopusta alkuun:");
   System.out.println(luku100); 
   System.out.println(luku99);
        .......           // nuo pisteet eivät ole Javaa!
   System.out.println(luku2); 
   System.out.println(luku1); 


Kovin kävisi työlääksi ohjelman laatiminen tuohon tapaan! Jokaista muuttujaa luku1, luku2, ..., luku100 käsitellään ihan samaan tapaan ja kuitenkin joudutaan kirjoittamaan kaikki käsittelyt erikseen!

Taulukon idea on, että yksi muuttujan nimi voi tarkoittaa kokonaista jonoa keskenään samantyyppisiä muuttujia. Kutakin tuollaista "alimuuttujaa" kutsutaan taulukon alkioksi eli komponentiksi. Yksittäisiin "alimuuttujiin" viitatataan indeksoimalla taulukkomuuttujaa:

Jos vaikkapa luku on tuollainen taulukkomuuttuja, ilmaus

   luku[7] 
tarkoittaa muuttujan luku "alimuuttujaa", jonka indeksi on 7. Tuota "alimuuttujaa" voidaan käsitellä ihan samaan tapaan kuin mitä tahansa samantyyppistä tavallista muuttujaa. Jos siis taulukkomuuttujan luku komponenttityyppi on int, voidaan kirjoittaa:
   luku[7] = 56;
   luku[7] += 3;
   luku[7] = luku[7]*luku[7];
   ++luku[7];
   luku[7] = Lue.kluku();
   System.out.println(luku[7]);
   
   jne, jne ....   

Äskeinen esimerkki voidaan ohjelmoida:

    int[] luku = new int[101]; // Tämä selviää pian!

    // luetaan 100 lukua:

    for (int i=1; i<=100; ++i) {
      System.out.println("Anna luku "+i);
      luku[i] = Lue.kluku();
    }

    // tulostetaan luvut käänteisessä järjestyksessä:

    System.out.println("Luvut lopusta alkuun:");
    for (int i=100; i>=1; --i)
      System.out.println(luku[i]);

Näin siis samalla ilmauksella
    luku[i]
tarkoitetaan i:n arvosta riippuen milloin mitäkin int-muuttujista
    luku[1], luku[2], ... luku[99], luku[100].

Huom: Indeksilauseke voi toki olla muutakin kuin pelkkä vakio tai muuttuja:
    luku[7+j] = luku[i-3] * luku[i*i];


Taulukko on olio

Edellisessä esimerkissä taulukko määriteltiin:
  int[] luku = new int[101];

[Huom: On valitettavasti mahdollista määritellä samaa tarkoittaen myös
  int luku[] = new int[101];
Onneton menettely on kotoisin eräistä Javan edeltäjistä. Koska edellinen tapa on paljon selkeämpi, kurssilla käytetään ainoastaan sitä!]

Taulukot ovat siis Javassa olioita: Ne luodaan konstruktorilla, niitä käsitellään viitteinä, ..., parametrina välitettyä taulukkoa voi muuttaa, metodi voi palauttaa arvonaan viitteen taulukkoon, ...

Sääntöjä:

  1. Taulukon indeksit ovat aina int-tyyppisiä.
  2. Taulukon ensimmäinen indeksi on aina 0.
  3. Taulukon koko on aina konstruktorin kutsussa annettu kokonaislukuarvo ja siis suurin käytössä oleva indeksi on koko-1 (koska ensimmäinen on aina 0).
  4. Taulukon käyttäjä on vastuussa siitä, että indeksinä ei käytetä koskaan arvoa joka ei ole välillä [0,koko-1]. Jos käytetään, ohjelman suoritus keskeytetään.
  5. Taulukon alkioilla on oletusalkuarvot (esim. 0, 0.0, false, null), koska alkiot ovat oikeastaan taulukko-olion kenttiä, ks. luku 4.2.
  6. Taulukko-olion koko ei voi koskaan muuttua. (Mutta taulukkomuuttuja voi tietenkin saada arvokseen uuden - vaikkapa erikokoisen - taulukon.)
  7. Taulukon koon saa selville ilmauksella .length Jos muuttujan luku arvona on 101-alkionen taulukko-olio, luku.length on 101. (Taulukko-olioilla on julkinen kenttä length.)
Koska taulukot siis aina alkavat indeksistä 0, on "Java-henkisempää" kirjoittaa edellä nähty esimerkki seuraavasti (KaannaLuvut.java):
public class KaannaLuvut {
  public static void main(String[] args) {

    int[] luku = new int[100];

    // luetaan 100 lukua:

    for (int i=0; i < luku.length; ++i) {
      System.out.println("Anna luku "+(i+1));
      luku[i] = Lue.kluku();
    }

    // tulostetaan luvut käänteisessä järjestyksessä:

    System.out.println("Luvut lopusta alkuun:");
    for (int i=luku.length-1; i >= 0; --i)
      System.out.println(luku[i]);
  }
}

Esimerkki (18.10.2004): Taulukon alkioiden läpikäynti on aina syytä tehdä yleisellä tavalla:
    for (int i=0; i < luku.length; ++i)
      ...
Näin ohjelmaa ei tarvitse muuttaa, jos taulukon kokoa halutaan muuttaa tai vaikkapa tehdä ohjelmasta yleisempi versio; taulukko-olio voidaan luoda myös kysytyn kokoiseksi (KaannaLuvut2.java):
public class KaannaLuvut2 {
  public static void main(String[] args) {

    int lukumäärä;

    do {
      System.out.println("Montako lukua käännetään? (Vähintään yksi!)");
      lukumäärä = Lue.kluku();
    } while (lukumäärä < 1);

    int[] luku = new int[lukumäärä]; // Huom: muuttuja!

    // Loppuosa ohjelmasta säilyy aivan ennallaan!

    for (int i=0; i < luku.length; ++i) {
      System.out.println("Anna luku "+(i+1));
      luku[i] = Lue.kluku();
    }

    // tulostetaan luvut käänteisessä järjestyksessä:

    System.out.println("Luvut lopusta alkuun:");
    for (int i=luku.length-1; i >= 0; --i)
      System.out.println(luku[i]);
  }
}


Esimerkkejä taulukkomuuttujien määrittelyistä, taulukko-olioiden konstruoinnista ja taulukoiden käytöstä:

public class TauluKokeita  {
  public static void main(String[] args) {

    // Määrittelyitä:

    int[] klukuA;               // taulukkomuuttuja, EI vielä taulukkoa!
    int[] klukuB = new int[10];

    int[] klukuC = {5, -23, 12, 31, 7}; 
                           // tämä ilmaus konstruoi taulukon int[5]
                           // ja asettaa alkioille alkuarvot!!
                           // Näitä voi käyttää VAIN muuttujia
                           // määriteltäessä!

    double[] dlukuA = new double[5];
    double[] dlukuB = {3.14, 7.21, 0.0, 9.1}; // double[4]!

    boolean[] totuusA = new boolean[8];
    boolean[] totuusB = {true, true, false};  // boolean[3]!

    String[] jonoA = new String[3];
    String[] jonoB = {"kissa", "hiiri"};      // String[2]!

    char[] merkkiA = new char[10];
    char[] merkkiB = {'q', 'w', 'e'};         // char[3]!

    // Käyttöä:

    for (int i=0; i<klukuB.length; ++i) // 7:n kertotaulu
       klukuB[i] = 7*(i+1);

    for (int i=0; i<klukuB.length; ++i)
       System.out.println(klukuB[i]);


    klukuA = klukuC;         // KOPIOIDAAN VIITE
    klukuA[1] = 123456;      // MYÖS klukuC[1] MUUTTUU!
    System.out.println(klukuA[1]+" "+klukuC[1]);

    klukuC = new int[5];  // luodaan uusi taulukko klukuC:n arvoksi
    klukuC[1] = -98;
    System.out.println(klukuA[1]+" "+klukuC[1]);
  }
}
Ohjelma tulostaa;
7
14
21
28
35
42
49
56
63
70
123456 123456
123456 -98


Ohjelmointitekniikkaa: etsiminen taulukosta

Melko tyypillinen ohjelmointitehtävä on sen selvittäminen, onko taulukossa kysyttyä alkiota.

Laaditaan metodi, joka saa parametrinaan int[]-tyyppisen arvon eli kokonaislukutaulukon ja selvittää, löytyykö taulukosta toisena parametrina annettua kokonaislukua. Jos kysytty arvo löytyy, metodi palauttaa haettavan luvun indeksin arvonaan (jos haettavia on taulukossa monia, palautetaan niistä ensimmäisen indeksi). Jos kysyttyä ei löydy, metodin arvo on -1 (Hae.java):

public class Hae {  // Peräkkäishaku int-taulukosta

  private static int hae(int[] taulu, int haettava) {

    for (int i=0; i<taulu.length; ++i) 
      if (taulu[i] == haettava)
        return i; // tärppäsi, lopetetaan

    return -1; // päästiin loppuun eikä löytynyt
  }

  public static void main(String[] args) { // testipääohjelma

    int[] a = {40, 20, 50, 10, 30};

    System.out.println(hae(a, 10));
    System.out.println(hae(a, 20));
    System.out.println(hae(a, 30));
    System.out.println(hae(a, 40));
    System.out.println(hae(a, 50));
    System.out.println(hae(a, 60));

    int[] b = new int[100];
    for (int i=0; i<b.length; ++i)
      b[i] = (int)(300*Math.random()); // arvotaan testisisältö
    System.out.println(hae(b, 123));
  }
}

Testiohjelma tulostaa (esim.):
  3
  1
  4
  0
  2
  -1
  26 

Yllä etsintä keskeytettiin return-lauseella heti kun etsittävä löytyi. Jos etsinnän jostain syystä haluaa toteuttaa keskeyttämättä for-lausetta, voi ohjelmoida:
  private static int hae(int[] taulu, int haettava) {
    int indeksi=0;
    boolean loytyi=false;
    int palautusindeksi = -1; // Pessimistinen oletus!

    while (!loytyi && indeksi < taulu.length) {
      if (taulu[indeksi] == haettava) {
        palautusindeksi = indeksi;
        loytyi = true;
      }
      ++indeksi;
    }
    return palautusindeksi;
  }



Jos taulukko on järjestyksessä, ns. binäärihaku on erittäin tehokas väline arvon hakemiseen taulukosta (BinHae.java):

public class BinHae {  // Binäärihaku järjestetystä 
                       // int-taulukosta

  private static int binHae(int[] taulu, int haettava) {

    int vasen = 0;
    int oikea = taulu.length-1;
    int keski;

    while (vasen <= oikea) {
      keski = (vasen+oikea)/2;

      if (haettava == taulu[keski])
        return keski;     // löytyi ja lopetetaan!

      if (haettava < taulu[keski])
        oikea = keski-1;
      else
        vasen = keski+1;
    }
    return -1;  // hakualue tuli tyhjäksi eikä löytynyt
  }

  public static void main(String[] args) { // testipääohjelma

    int[] a = {10, 20, 30, 40, 50};

    System.out.println(binHae(a, 5));
    System.out.println(binHae(a, 10));
    System.out.println(binHae(a, 20));
    System.out.println(binHae(a, 30));
    System.out.println(binHae(a, 35));
    System.out.println(binHae(a, 40));
    System.out.println(binHae(a, 50));
    System.out.println(binHae(a, 60));

  }
}
Tulostus:
-1
0
1
2
-1
3
4
-1


Ohjelmointitekniikkaa: taulukon järjestäminen

Taulukon alkioiden järjestäminen on myös usein tarvittu toimenpide. Järjestämisalgoritmeja on monenlaisia, tehokkaita ja vähemmän tehokkaita.

Yksi tehoton, mutta helposti ymmärrettävä järjestämisalgoritmi on ns. yksinkertainen vaihtojärjestäminen (nousevaan järjestykseen) (VaihtoJarj.java):

public class VaihtoJarj { // Yksinkertainen vaihtojärjestäminen

   private static void vaihtoJarjesta(int[] taulu) {
     for (int i=0; i < taulu.length-1; ++i) 
       for (int j=i+1; j < taulu.length; ++j)
         if (taulu[i] > taulu[j]) { // onko järjestys väärä?
            int apu = taulu[i];
            taulu[i] = taulu[j];
            taulu[j] = apu;
         }
   }

  public static void main(String[] args) { // testipääohjelma

    int[] a = {40, 20, 50, 10, 30};

    for (int i=0; i<a.length; ++i)
       System.out.print(a[i]+" ");
    System.out.println();

    vaihtoJarjesta(a);

    for (int i=0; i<a.length; ++i)
       System.out.print(a[i]+" ");
    System.out.println();

    int[] b = new int[18];
    for (int i=0; i<b.length; ++i)
      b[i] = (int)(300*Math.random()); // vrt. harj. 15

    for (int i=0; i<b.length; ++i)
       System.out.print(b[i]+" ");
    System.out.println();

    vaihtoJarjesta(b);

    for (int i=0; i<b.length; ++i)
       System.out.print(b[i]+" ");
    System.out.println();
  }
}
Tulostus (esimerkiksi):
40 20 50 10 30
10 20 30 40 50
66 250 88 15 124 97 165 119 233 147 220 161 137 185 219 2 5 295
2 5 15 66 88 97 119 124 137 147 161 165 185 219 220 233 250 295

Yksinkertaisen vaihtojärjestämisen (vaikkapa nousevaan järjestykseen) idea on seuraava: Toinen tehoton järjestämismenetelmä on ns. kuplajärjestäminen (bubble sort). Sen idean voisi kuvailla seuraavasti: Kuplajärjestäminen voidaan ohjelmoida vaikka (KuplaJarj.java):

   private static void kuplaJarjesta(int[] taulu) {
     for (int i=taulu.length; i > 0; --i) 
       for (int j=0; j < i-1; ++j)
         if (taulu[j] > taulu[j+1]) { // onko järjestys väärä?
            int apu = taulu[j];
            taulu[j] = taulu[j+1];
            taulu[j+1] = apu;
         }
   }


Edellisiä hieman parempi järjestämisalgoritmi on ns. lisäysjärjestäminen (insertion sort) (LisaysJarj.java):

  private static void lisaysJarjesta(int[] taulu) {
    for (int i=1; i<taulu.length; ++i) {
      int apu = taulu[i];
      int j = i;
      while (j > 0 && taulu[j-1] > apu) {
        taulu[j] = taulu[j-1]; 
        --j;
      }
      taulu[j] = apu;
    }
  }

Järjestämisalgoritmien (kuten muidenkin algoritmien!) tehokkuutta voidaan analysoida matemaattisesti: kaikki edelläesitetyt järjestämisalgoritmit kuuluvat ns. luokkaan O(n2). Se tarkoittaa suurinpiirtein, että "jos järjestettävän taulukon koko kaksinkertaistetaan, algoritmin suoritusaika nelinkertaistuu". Parhaat järjestämisalgoritmit "toimivat ajassa O(n*log n)" ns. "yleisessä tapauksessa".

Toisaalta suoritusaikoja voidaan myös kokeellisesti mitata. Pieni vertailu antaa seuraavia suoritusaikoja kun järjestetään 50000-alkoinen int-taulukko (ohjelma VertaileJarj.java) ("testiohjelmasta" on uusi versio 20.10.2003 VertaileJarj2.java) (ajat riippuvat tietysti käytettävästä tietokoneesta, mutta suhteet säilynevät, tässä on kyse sekunneista ja koneen prosessorina on Pentium 4 1.6 GHz):

                arvottu          valmiiksi      käänteisessä
                alkujärjestys    järjestetty    järj. oleva
                                 taulukko       taulukko
              
  vaihtojärj.       10             10              20

  kuplajärj.        20              8              16

  lisäysjärj.        5            << 1             10

Kuten taulukosta näkee, lisäysjärjestäminen on muita paljon nopeampi vaikka se kuuluukin niiden kanssa samaan "tehokkuusluokkaan". Ja erityisesti melkein järjestyksessä olevien taulukoiden järjestämisessä se on ylivoimainen.

String-olioita taulukossa

Taulukko on siis itse olio: se luodaan new-ilmauksella, viite siihen voi olla muuttujan arvona, viite taulukkoon voidaan välittää parametrina, metodi voi palauttaa arvonaan viitteen taulukkoon, ...

Myös taulukon komponentit voivat olla olioita eli tarkemmin sanottuna viitteitä olioihin!

String-olioita sisältävä taulukkomuuttuja voidaan määritellä:

   String[] taulu;
Metodin muuttujana taululla ei ole mitään alkuarvoa, luokassa määriteltynä sillä on alkuarvo null.

  String[] taulu = new String[5]; 
asettaa muuttujan taulu alkuarvoksi viisialkioisen String-taulukko-olion. Taulukon alkioilla on aina oletusalkuarvo null.

Lause

  String[] taulu = {"kissa", "hiiri", "koira"};
asettaa muuttujan taulu alkuarvoksi kolmialkioisen String-taulukko-olion, jonka alkioille annetaan alkuarvot.

Huom: Pääohjelmametodin pakollinen parametri

  String[] args
on siis String-taulukko. Sen alkuarvoksi asetetaan ohjelman käynnistyskomentoa seuraavat välilyönnein toisistaan erotetut merkkijonot, ns. komentoriviparametrit. Jos ohjelma Ohjelma käynnistetään komennolla
  java Ohjelma apina ja gorilla
pääohjelman args-parametri saa alkuarvokseen kolmialkioisen String-taulukon, jonka alkioilla on arvot "apina", "ja" ja "gorilla".

Esimerkkejä String-taulukoista (STauluKoe.java):

public class STauluKoe {

  private static void tulostaSTaulu(String[] taulu) {
    System.out.println("\n ---- taulukon koko: "+taulu.length+":");
    for (int i=0; i<taulu.length; ++i)
      System.out.println(taulu[i]);
  }

  public static void main(String[] args) {

    String[] ekaTaulu,
             tokaTaulu = new String[5],
             kolmasTaulu = {"kissa", "hiiri", "koira"};

    tulostaSTaulu(kolmasTaulu);

    tulostaSTaulu(tokaTaulu);  // null-alkuarvot

    for (int i=0; i<tokaTaulu.length; ++i)
      tokaTaulu[i] = "(" + i + ")";

    tulostaSTaulu(tokaTaulu);

    //  tulostaSTaulu(ekaTaulu); OLISI TÄSSÄ VIRHEELLINEN!

    ekaTaulu = kolmasTaulu;    // kopioidaan viite!
    tulostaSTaulu(ekaTaulu);

    ekaTaulu = tokaTaulu;
    tulostaSTaulu(ekaTaulu);

    tulostaSTaulu(args);       // komentoriviparametrit    
  }
}

Kun ohjelma käynnistetään komennolla:
   java STauluKoe apina ja gorilla
saadaan tulostus:

 ---- taulukon koko: 3:
kissa
hiiri
koira

 ---- taulukon koko: 5:
null
null
null
null
null

 ---- taulukon koko: 5:
(0)
(1)
(2)
(3)
(4)

 ---- taulukon koko: 3:
kissa
hiiri
koira

 ---- taulukon koko: 5:
(0)
(1)
(2)
(3)
(4)

 ---- taulukon koko: 3:
apina
ja
gorilla


String-oliot ja char-taulukot

String-luokan oliot ovat kerran synnyttyään muuttumattomia.

Luokka StringBuffer tarjoaa joukon kehittyneitä välineitä merkkijonoarvojen muutteluun (StringBuffer-luokan virallinen määrittely, suuri tiedosto!).

Koska on kuitenkin hyödyllistä oppia tekemään asiat "omin käsin", tässä tutustutaan merkkitaulukon käyttämiseen merkkijonojen käsittelyssä:

Määrittelyt

    char[] jono1 = new char[10];
    char[] jono2  = {'k','i','s','s','a'};
asettavat muuttujan jono1 arvoksi 10-alkioisen merkkitaulukon ja muuttujan jono2 arvoksi 5-alkioisen merkkitaulukon. Jälkimmäisen alkioille annetaan alkuarvot, edellisen alkiot saavat oletusalkuarvon (merkkityypin oletusalkuarvo taulukossa ja luokan kenttänä on ns. null-merkki '\u0000').

Nyt voidaan ohjelmoida vaikkapa:

     for (int i=0; i<jono2.length; ++i)
        System.out.print(jono2[i]);
     System.out.println(); 

     for (int i=0; i<jono1.length; ++i)
        jono1[i] = '*';

     for (int i=0; i<jono2.length; ++i)
       jono1[9-i] = jono2[i];

     for (int i=0; i<jono1.length; ++i)
       System.out.print(jono1[i]);
     System.out.println(); 
Lauseet tulostavat:
kissa
*****assik


String-olioita voi muuntaa char[]-taulukoiksi String-luokan metodilla: Merkkitaulukosta puolestaan voi luoda String-olion konstruktorilla: Laaditaan esimerkkiohjelma, joka lukee merkkijonon ja tulostaa sen sellaisena, jossa kaikki 'a'-merkit on korvattu merkillä 'ä' (Aeta.java):
public class Aeta { 
  public static void main(String[] args) {

    System.out.println("Anna merkkijono");
    String jono = Lue.rivi();

    char[] mtaulu = jono.toCharArray();

    for (int i=0; i<mtaulu.length; ++i)
      if (mtaulu[i] == 'a')
        mtaulu[i] = 'ä';

    jono = new String(mtaulu);

    System.out.println("Merkkijono muunnettuna:");
    System.out.println(jono);
  }
}

Huom: Merkkijonotaulukko (char[]-olio) on mahdollista tulostaa sellaisenaankin (koska println-metodi on kuormitettu!):
    System.out.println(mtaulu);


Omia olioita taulukossa

Myös omia tyyppejä voidaan käyttää taulukon komponenttityyppinä! Esimerkiksi määrittely
   PikkuVarasto[] varastot = new PikkuVarasto[4];
määrittelee PikkuVarasto[]-tyyppisen muuttujan ja asettaa sen alkuarvoksi neljästä PikkuVarasto-tyyppisestä muuttujasta muodostuvan taulukon. Taulukon alkioiden alkuarvo on null. Toisin sanoen: nelialkionen taulukko-olio luodaan ja sijoitetaan muuttujan varastot arvoksi, mutta taulukon alkioiden arvona ei vielä ole PikkuVarasto-olioita! Niitä voidaan sitten luoda erikseen:
   varastot[0] = new PikkuVarasto(10, "Mehu");
   varastot[1] = new PikkuVarasto(123.4, "Olut");
   ...

Tämän jälkeen taulukon alkioita - noita luotuja PikkuVarasto-olioita - voidaan käyttää aivan samaan tapaan kuin aiemmin:
   varastot[0].vieVarastoon(30.0);
   System.out.println(varastot[0].paljonkoOn());
   varastot[0].otaVarastosta(7.0);
   System.out.println(varastot[0]);

   varastot[2] = varastot[0];
   System.out.println(varastot[2].mikaNimi());

Taulukko-olioita taulukossa

Koska Javan taulukoiden komponenttityyppi voi olla mikä tahansa tyyppi, myös taulukkotyyppi voi olla taulukon komponenttina! Näin saadaan luotua ns. moniulotteisia taulukoita. Tässä luvussa tutustutaan vain kaksiulotteisiin taulukoihin. Niitä kutsutaan toisinaan matriiseiksi.

Huom: Oikeastaan Javassa on vain yksiulotteisia taulukoita, mutta koska taulukon komponentit (ja niiden komponentit!) voivat olla taulukoita, ohjelmoija voi ajatella käytävänsä useampiulotteisia taulukoita.

Määrittely

    int[][] matriisiA;
tarkoittaa, että muuttujan matriisiA tyyppi on int[]-taulukoista muodostuva taulukko.

Määrittely

    int[][] matriisiA = new int[2][2];
asettaa muuttujan alkuarvoksi 2*2 matriisin, jonka alkioiden alkuarvo on 0.

Määrittely

    int[][] matriisiB = { {1, 2}, {3,4} };
puolestaan luo muuttujan matriisiB arvoksi 2*2 matriisin annetuin alkuarvoin.

Myös kaksiulotteisen taulukon alkioihin viitataan indeksoiden:

    matriisiB[0]
tarkoittaa int[]-taulukkoa
    {1, 2}
Ilmaus
    matriisiB[0][1]
puolestaan tarkoittaa int-alkiota, jonka arvo on 2.

Kaksiulotteisen taulukon alkiot voidaan läpikäydä vaikkapa lauseella:

    for (int rivi=0; rivi<matriisi.length; ++rivi)
      for (int sarake=0; sarake<matriisi[rivi].length; ++sarake) 
        // alialgoritmi yhden alkion käsittelyyn

Laaditaan esimerkkisovellus, joka lukee 2*2-matriisin ja tulostaa sen 7:llä kerrottuna (so. jokainen alkio kerrotaan 7:llä) (Matriisi7.java):

public class Matriisi7 {
  public static void main(String[] args) {

    int[][] matriisi = new int[2][2];

    // luetaan arvot:

    for (int rivi=0; rivi<matriisi.length; ++rivi)
      for (int sarake=0; sarake<matriisi[rivi].length; ++sarake) {
        System.out.println("Anna alkio "+rivi+", "+sarake);
        matriisi[rivi][sarake] = Lue.kluku();
      }

    // kerrotaan alkiot 7:llä:

    for (int rivi=0; rivi<matriisi.length; ++rivi)
      for (int sarake=0; sarake<matriisi[rivi].length; ++sarake)
        matriisi[rivi][sarake] *= 7;  // operaatio '*='kertoo!

    // tulostetaan matriisi:

    System.out.println("Seitsemällä kerrottuna:");
    for (int rivi=0; rivi<matriisi.length; ++rivi) {
      for (int sarake=0; sarake<matriisi[rivi].length; ++sarake)
        System.out.print(matriisi[rivi][sarake]+"\t");
      System.out.println();
    }

  }
}


Taulukko luokan kenttänä

Kuten muitakin tyyppejä myös taulukkotyyppiä voi käyttää luokan kenttänä.

Toteutetaan luokka AlaspainLaskureita, sadasta alaspäin laskevien laskureiden toteutukseksi:

Toteutus (AlaspainLaskureita.java):
public class AlaspainLaskureita {

  // tietorakenne:

  private int[] laskurit;

  // konstruktori:

  public AlaspainLaskureita(int kpl) {
    if (kpl<=1) 
      laskurit = new int[1];
    else
      laskurit = new int[kpl];
    for (int i=0; i<laskurit.length; ++i)
      laskurit[i] = 100;  
  }

  public int seuraava(int n) {
    if (n<0 || n>=laskurit.length)
      return -1;
    int arvo = laskurit[n];
    if (laskurit[n]>0) 
      --laskurit[n];
    return arvo;
  }

  // testipääohjelma:

  public static void main(String[] args) {
    
    AlaspainLaskureita a = new AlaspainLaskureita(10);
    AlaspainLaskureita b = new AlaspainLaskureita(-10);

    // vähentyminen:
    System.out.println(a.seuraava(3));
    System.out.println(a.seuraava(3));
    System.out.println(a.seuraava(3));

    // virheellinen alkio:
    System.out.println(a.seuraava(12));

    // loppuuko väheneminen:
    for (int i=0; i<98; ++i) {
       int apu = a.seuraava(7);
    }
    System.out.println(a.seuraava(7));
    System.out.println(a.seuraava(7));
    System.out.println(a.seuraava(7));
    System.out.println(a.seuraava(7));

    // tuliko yhden mittaiseksi väärästä konstruoinnista:
    System.out.println(b.seuraava(0));
    System.out.println(b.seuraava(0));
    System.out.println(b.seuraava(1));
  }
}
Testipääohjelma tulostaa:
100
99
98
-1
2
1
0
0
100
99
-1


Takaisin luvun 2 sisällysluetteloon.