Helsingin yliopisto / Tietojenkäsittelytieteen laitos / 581258-1 Johdatus ohjelmointiin
Copyright © 2001 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.

3.6 Taulukoista

(Muutettu viimeksi 13.11.2001)

Javassa taulukot ovat olioita. Ne siis muiden olioiden tapaan luodaan new-operaatiolla (myös ns. taulukon alustajalla, kts. alla). Muiden oliomuuttujien tapaan taulukkomuuttuja on viittaustyyppiä. Kun taulukko-olio on luotu, sen kokoa ei voi muuttaa, mutta taulukkomuuttuja voi ohjelman suorituksen aikana saada arvokseen eri taulukko-olioita, esimerkiksi eri mittaisia taulukoita!

Taulukon koko on siis taulukko-olion ominaisuus, ei taulukkomuuttujan.

Taulukon indeksit alkavat aina nollasta ja indeksien arvot voivat olla vain int-tyyppisiä (byte, short, ja char kelpaavat, ne muutetaan automaattisesti int-tyyppisiksi, mutta long on itse muutettava eksplisiittisellä tyyppimuunnoksella!).

Taulukkomuuttujan määrittely ja taulukko-olion luonti

Kun kirjoitetaan:
   int[] tauluA;
   int[] tauluB = null;
   int[] tauluC = new int[4];
   int[] tauluD = {3, 5, 9, 11};
   int[] tauluE = {};
tahdotaan sanoa, että

Taulukkomuuttujan määrittelyssä käytettävät []-sulkeet voidaan sijoittaa joko tyypin tai muuttujan jälkeen. Esimerkiksi määrittelyt

  String[] args;
  String args[];
tarkoittavat täsmälleen samaa: muuttuja args voi joskus saada arvokseen taulukon, jonka komponentit ovat String-olioita. Ensimmäinen muoto ilmaisee selkeämmin, että muuttujan tyyppi on taulukko!

Myös useampiulotteiset taulukot ovat mahdollisia (kyseessä ovat oikeastaan yksiulotteiset taulukot, joiden komponentteina on taulukoita!):

  int[][] matriisiA = new int[2][2];
  int[][] matriisiB = { {1, 2}, {3,4} };

  double[][][] avaruus = new double[100][100][100];

  short[][][] mikälie = {{{1,2},{3,4}},
                         {{5,6},{7,8}}
                        };

  boolean[][][][][] böö = new boolean[2][3][54][2][9];
Huom: on syytä tietää (mutta näin ei pidä itse tehdä!), että määrittelyt:
  double avaruus[][][] = new double[100][100][100];
  double[] avaruus[][] = new double[100][100][100];
  double[][] avaruus[] = new double[100][100][100];
tarkoittavat aivan samaa kuin edellisen esimerkinkin määrittely muuttujalle avaruus!

Kuten edellä todettiin, taulukkotyypillä ei ole pituutta eli komponenttien määrää, mutta taulukko-olio on koko olemassaolonsa ajan vakiokokoinen. Taulukko-olion pituus löytyy kentästä length (luokista ja kentistä luvussa 4):

  double[] dTau = new double[10];

  // nyt dTau.length == 10

  dTau = new double[100];

  // nyt dTau.length == 100
Niinpä on mahdollista välittää vaikkapa parametrina vaihtelevankokoisia taulukoita (joiden komponenttityyppi on sama!):
  private static void ykköstä(double[] tau) {
    for (int i=0; i<tau.length; ++i)
      tau[i] = 1.0;
  }
Huom: Taulukon pituus saadaan kentästä length, String-olion pituus metodilta length()!

Taulukon käyttö

Taulukon komponentti valitaan indeksoimalla taulukkoa, kirjoittamalla int-tyyppiseksi kelpaava lauseke []-sulkeisiin taulukkomuuttujan nimen perään. Jos taulukkomuuttuja tau on asetettu viittaamaan johonkin taulukko-olioon ja jos indeksilausekkeen ind arvo on oikean kokoinen, tau[ind] tarkoittaa taulukon komponenttityypin muuttujaa.

Taulukon komponenttityyppi voi olla mikä tahansa Javan tyyppi, myös viittaustyyppi! Niinpä siis alkeistyyppien lisäksi vaikkapa String on mahdollinen ja myös mikä tahansa itse tehty luokka!

Taulukko siis on itse olio ja se luodaan new-operaatiolla (tai alustajalla). Jos taulukon komponentit ovat olioita, myös ne on luotava kukin erikseen new-operaatiolla! (Oliokomponentin alkuarvo on tyhjä olio, null. Myös alkeistyyppiset komponentit saavat oletusalkuarvon, kts. luku 4.)

Esimerkkejä taulukon käytöstä (TKayttoa.java):

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

    int[] itauA;
    int[] itauB = null;
    int[] itauC = new int[10];

    String[] mjtau = new String[20];

    PikkuVarasto[] vtau = new PikkuVarasto[100];
                       // taulukko, jonka joka alkioksi  
                       // voidaan asettaa PikkuVarasto-olio!!

    itauC[2] = 7;
    itauC[0] = itauC[2];
    itauC[9] = itauC[2] - 89;

    itauA = itauB;

    itauA = new int[30];

    itauB = itauA;

    mjtau[0] = "Miau";
    for (int i=1; i<6; ++i)
      mjtau[i] = mjtau[i-1]+mjtau[0];

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


    for (int i=0; i<100; ++i)
      vtau[i] = new PikkuVarasto(i, "Olut"); // i:nteen varastoon
                                            // arvoksi i

    System.out.println(vtau[7]);

    vtau[7].vieVarastoon(vtau[42].paljonkoOn());

    System.out.println(vtau[7]);
  }
}

Ohjelma tulostaa:
Miau
MiauMiau
MiauMiauMiau
MiauMiauMiauMiau
MiauMiauMiauMiauMiau
MiauMiauMiauMiauMiauMiau
(Olut: 7.0)
(Olut: 49.0)

Jos oletetaan äskeiset esimerkkimäärittelyt:

  int[] itauA;
  int[] itauB = null;
  int[] itauC = new int[10];

  String[] mjtau = new String[20];

  PikkuVarasto[] vtau = new PikkuVarasto[100];
                       // taulukko, jonka joka alkioksi  
                       // voidaan asettaa PikkuVarasto-olio!!

seuraavat lauseet ovat virheellisiä:
  itauA[2] = 7;
     //Kääntäjä: Variable itauA may not have been initialized.

  itauB[2] = 7;
     //Tulkki: java.lang.NullPointerException

  itauC[10] = 3;
     //Tulkki: java.lang.ArrayIndexOutOfBoundsException

  itauC[9] = 89.9;
     //Kääntäjä: Incompatible type for =. Explicit cast needed to
     //          double to int.

  itauA = new double[30];
     //Kääntäjä: Incompatible type for =.  Can't convert double[]
     //          to int[].

  itauB = itauA;
     //Kääntäjä: Variable itauA may not have been initialized.

//----

   vtau[7].vieVarastoon(321);
     //Tulkki: java.lang.NullPointerException

Komentoriviparametrit

Main-metodin parametri args on tyyppiä String[]. Sovelluksen käynnistyessä args-taulukko saa arvokseen komentorivillä ohjelman nimeä seuraavat merkkijonot, ns. komentoriviparametrit:
  public class Parat {
    public static void main(String[] args) {
      for (int i=0; i < args.length; ++i)
        System.out.println(i+". parametri: "+args[i]);
    }
  }
Kun sovellus käynnistetään komennolla:
java Parat Olipa kerran kissa.
tulostuu:
0. parametri: Olipa
1. parametri: kerran
2. parametri: kissa.
"Merkkijonot" tässä yhteydessä tarkoittavat välilyönnein erotettuja välilyöntejä sisältämättömiä merkkien jonoja.

Useampiulotteiset taulukot

Koska taulukon komponenttityyppi voi olla mikä tahansa Javan tyyppi, komponentit voivat olla myös taulukkotyyppiä. Oikeastaan taulukot ovat aina yksiulotteisia, mutta niiden komponentit voivat olla taulukoita, jne...

(Java-terminologiassa taulukon komponenttityypiksi (component type) kutsutaan mitä tahansa tyyppiä josta taulukko on muodostettu. Alkiotyypiksi (element type) kutsutaan sellaista komponenttityyppiä, joka ei ole taulukko. Jokaisesta taulukkotyypistä löytyy lopulta alkiotyyppi. Sitä kutsutaan koko taulukon alkiotyypiksi. Samalla tavoin käytetään käsitteitä komponentti ja alkio!)

Esimerkki matriiseista:

  int[][] a = new int[2][3];
  int[][] b = {{9, 21, -2}, {2, 7, 19}};
  int[][] c = new int[2][3];

  for (int i=0; i<2; ++i)
    for (int j=0; j<3; ++j)
      a[i][j] = i+j;

  for (int i=0; i<2; ++i)
    for (int j=0; j<3; ++j)
      c[i][j] = a[i][j] + b[i][j];

  for (int i=0; i<2; ++i) {
    for (int j=0; j<3; ++j)
      System.out.print(c[i][j]+" ");
    System.out.println();
  }
Ohjelma tulostaa:
9 22 0
3 9 22

Taulukkotyyppi siis määrää vain taulukon komponenttityypin, se ei määrää taulukon kokoa. Jokainen viittaustyyppinen komponenttiolio luodaan erikseen. Kun taulukon komponenttityyppi on taulukkotyyppi, eri komponenteiksi voidaan luoda eri kokoisia taulukko-olioita:
  int[][] c = new int[2][];

  // c on int[][]-tyyppinen taulukkomuuttuja, jonka 
  // komponentteina on nyt kaksi int[]-taulukkoa: 
  // c[0] ja c[1]

  // luodaan komponenttiolioiksi eri kokoiset taulukot:

  c[0] = new int[5];
  c[1] = new int[2];

  // käytetään taulukkoja:

  for (int i=0; i < c[0].length; ++i)
    c[0][i] = 100-5*i;

  for (int i=0; i < c[1].length; ++i)
    c[1][i] = 5*i - 100;

  for (int i=0; i < c.length; ++i) {
    for (int j=0; j < c[i].length; ++j)
      System.out.print(c[i][j]+" ");
    System.out.println();
  } 
Ohjelma tulostaa:
100 95 90 85 80
-100 -95

Toinen esimerkki:
    int[][][][] t = new int[3][][][];
 
    t[1] = new int[4][][];
 
    t[1][3] = new int[3][];
 
    t[1][3][0] = new int[2];
    t[1][3][2] = new int[5];
 
    t[1][3][0][1] = 8;
    t[1][3][2][3] = -123;
Piirrä kuva tästä!

Taulukkotyyppiset metodit

Metodin tyyppi voi olla mikä tahansa Javan alkeistyyppi tai viittaustyyppi. Niinpä metodi voi palauttaa arvonaan vaikkapa taulukon (TauluMetossa.java):
public class TauluMetossa {

  public static void main(String[] args) {

     int[] b = teePerustaulu(20);

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

  private static int[] teePerustaulu(int koko) {

      int[] tulos = new int[koko];

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

      return tulos;
  }
}
Ohjelma tulostaa:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Mutkikkaammatkin tapaukset ovat mahdollisia (IhmeTauluMetossa.java):
public class IhmeTauluMetossa {

  public static void main(String[] args) {

    String[][] c = abcMatriisi('A', 5);

    for (int i=0; i < c.length; ++i) {
      for (int j=0; j < c[i].length; ++j)
        System.out.print(c[i][j]+" ");
      System.out.println();
    }
  }

  private static String[][] abcMatriisi(char merkki, int kpl) {

      String[][] tulos = new String[kpl][kpl];

      for (char m=merkki; m < merkki+kpl; ++m)
        for (char n=merkki; n < merkki+kpl; ++n)
          tulos[m-merkki][n-merkki] = ""+ m+n;

      return tulos;
  }
}
Ohjelma tulostaa:
AA AB AC AD AE
BA BB BC BD BE
CA CB CC CD CE
DA DB DC DD DE
EA EB EC ED EE



Takaisin luvun 3 sisällysluetteloon.