Taulukko se taasen tuttu
oiva säilö alkioille
maja muuttujalle monelle
silmäiltäväksi silmukalla.
Mitä tässä luvussa käsitellään?
· Java–kielen taulukot
· taulukoiden ja viitteiden yhteys
· moniulotteiset taulukot
Syntaksi:
Taulukon esittely: alkiontyyppi taulukonnimi[];
Taulukon luominen: taulukonnimi = new alkiontyyppi[koko_alkioina]
Alkioon viittaaminen: taulukonnimi[alkion_indeksi]
Muista 1. indeksi = 0
viimeinen = koko_alkiona-1
Silmukoissa for (i=0; i<taulukonnimi.length; i++) ...
2-ul.taulukon es: alkiontyyppi taulukonnimi[][];
2-ul taul. luominen: taulukonnimi = new alkiontyyppi[riveja][sarakkeita]
Luvun esimerkkikoodit:
C–kielessä taulukoita ei oikeastaan ole, tai ainakin ne ovat '2. luokan kansalaisia'. Lausuma tarkoittaa sitä, että taulukoista on käytettävissä vain 1. alkion osoite ja esimerkiksi taulukon sisällön sijoittaminen toiseen taulukkoon ei onnistu sijoitusoperaattorilla. Lisäksi taulukon rajoissa pysymiselle ei ole minkäänlaista valvontaa.
Javassa onneksi taulukot on tehty hieman paremmin. Erityisesti kriittisistä rajojen ylityksistä tulee poikkeus.
Taulukko määritellään kertomalla taulukon alkioiden tyyppi ja luomalla sitten varsinainen taulukko:
int k_pituudet[]; // saadaan vasta viite jolla voidaan viitata taulukkoon
kpituudet = new int[12]; // luodaan taulukko;
Tällöin taulukon 1. alkion indeksi on 0 ja 12. alkion indeksi on 11.
Määrittelyllä muuttujasta k_pituudet tulee osoitin kokonaislukuun; taulukon alkuun.
Taulukon alkioon voidaan viitata alkion indeksin avulla
k_pituudet[0]=31; /* tammikuu */
k_pituudet[1]=28; /* helmikuu */
k_pituudet[2]──┐
│
k_pituudet │
│ v
│ 0 1 2 3 4 5 6 7 8 9 10 11
│ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
└───>│31│28│31│30│31│30│31│31│30│31│30│31│
└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
Taulukon rajojen ylityksestä seuraa IndexOutOfBoundsException-poikkeus
k_pituudet[24]=31;
eli 2 paikkaa eteenpäin taulukon alusta lukien.
Taulukko voitaisiin nollata seuraavalla silmukalla:
for (int i=0; i<k_pituudet.length; i++)
k_pituudet[i]=0;
Huomautus! Taulukoiden käsittelyssä on muistettava, että indeksi liikkuu välillä [0,YLÄRAJA[.
Taulukko voidaan alustaa (vain) esittelyn yhteydessä:
/* 1. 2. 3. 4. 5. 6. 7. 8. 9.10.11.12 */
int k_pituudet[] = {31,28,31,30,31,30,31,31,30,31,30,31};
Tehtävä 13.1 Taulukon alkioiden summa
Kirjoita funktio–aliohjelma taulukon_summa, joka palauttaa taulukon alkioiden summan. Kirjoita pääohjelma, jossa aliohjelmaa kutsutaan valmiiksi alustetulla taulukolla k_pituudet ja tulostetaan vuoden päivien lukumäärä.
Merkkijonot ovat eräs ohjelmoinnin tärkeimmistä tietorakanteista. Valitettavasti tämä on lähes poikkeuksetta unohtunut ohjelmointikielten tekijöiltä. Heille riittää että kielellä VOI tehdä merkkijonotyypin. Tavallista käyttäjää kiinnostaa tietysti onko se tehty ja onko se hyvä. Usein vastaus on EI. Näin myös C–kielen kohdalla! C++:han on jo välttävä merkkijonoluokka. Javassa on kaksi merkkijonoluokkaa String ja StringBuffer.
Yksittäinen merkki on Java–kielessä tyyppiä char:
char rek_1_merkki;
rek_1_merkki = 'X';
Merkkimuuttujiin voidaan vallan hyvin sijoittaa myös merkin koodiarvo
char m;
m = 65;
if ( m == 'A' ) ...
Lukuarvo tarkoittaa merkin (UNICODE–) koodia.
Javan String-luokka tarjoaa muuttumattoman merkkijonon (immutable). Merkkijonon "sisältö" voidaan vaihtaa vain luomalla uusi merkkijono.
Jos halutaan merkkijono, jonka sisältöä voidaan muuttaa (mutable), pitää käyttää StringBuffer-luokkaa.
Moniulotteiset taulukot ovat Javassa vain yksiulotteisia taulukoita taulukoista.
Kaikkein helpoin tapa esitellä moniulotteinen taulukko on aivan normaali esittely:
int matriisi[][] = new int[3][4]
┌───┬───┬───┬───┐
matriisi[0]──>│ │ │ │ │─── matriisi[0][3]
├───┼───┼───┼───┤
matriisi[1]──>│ │ │ │ │
├───┼───┼───┼───┤
matriisi[2]──>│ │ │ │ │
└───┴───┴───┴───┘
Taulukon nimi on vain viite taulukkoon. Taulukko on yksiulotteinen taulukko riveistä. Edellä
matriisi.length == 3
matriisi[1].length == 4
Taulukon alkioina voi tietysti olla mikä tahansa olemassa oleva tyyppi. Myös moniulotteinen taulukko voidaan alustaa esittelyn yhteydessä:
double yks[][] = {
{ 1.0, 0.0, 0.0 },
{ 0.0, 1.0, 0.0 },
{ 0.0, 0.0, 1.0 }
}
Tehtävä 13.2 Matriisit
Kirjoita seuraavat aliohjelmat, jotka saavat parametrinään 2 nxn matriisia ja palauttavat nxn matriisin:
1. Laskee yhteen 2 matriisia.
2. Kertoo kaksi matriisia keskenään. (Kirjoita avuksi funktio, joka kertoo matriisin rivin i toisen matriisin sarakkeella j).
Toisaalta moniulotteinenkin taulukko voidaan toteuttaa 1–ulotteisena. Tästä muunnoksestahan puhuttiin jo monisteen alkuosassa. On makuasia kumpiko järjestys esimerkiksi matriisissa valitaan: sarakelista vaiko rivilista. Rivilista on C–kielen mukainen, mutta toisaalta maailma on pullollaan Fortran aliohjelmia, joissa matriisit on talletettu sarakelistana. Siis kumpikin tapa on syytä hallita.
Tehtävä 13.3 Matriisi 1–ulotteisena
Kirjoita aliohjelma tee_yksikko, jolle tuodaan parametrinä neliömatriisin rivien lukumäärä ja 1–ulotteisen taulukon viite, ja joka alustaa tämän neliömatriisin yksikkömatriisiksi.
Javassahan moniuloitteinen taulukko on tosiasiassa taulukko taulukoista.
/**
* Matriisi parametrina
* @author Vesa Lappalainen
* @version 1.0, 04.03.2003
*/
public class Mat2 {
public static double alkioiden_summa(double mat[][]) {
double summa = 0; int riv = mat.length;
for (int i=0; i<riv; i++) {
int sar = mat[i].length;
for (int j=0; j<sar; j++)
summa += mat[i][j];
}
return summa;
}
public static void main(String[] args) {
double s1,s2,s3;
double yks[][] = {
{ 1.0, 0.0, 0.0 },
{ 0.0, 1.0, 0.0 },
{ 0.0, 0.0, 1.0 }
};
double mat2[][] = { {1,2,3,4},{5,6,7,8} },
mat3[][] = { {1,0,0},{0,1,0},{0,0,1} };
s1 = alkioiden_summa(yks);
s2 = alkioiden_summa(mat2);
s3 = alkioiden_summa(mat3);
System.out.println("Summat ovat " + s1 + ", " + s2 + " ja " + s3);
}
}
Se että matriisi onkin vain taulukko viitteistä riveihin, mahdollistaa edellä olleen mielivaltaisen kokoisen matriisin käyttämisen aliohjelman parametrina. Matriisin rivit voidaan luoda myös erikseen:
mat─┐ mat[0] ┌────────── mat[0][2]
│ │ │
│ │
v └───>┌───┬───┬───┬───┐
┌───┐ ┌────>│ 1 │ 2 │ 3 │ 4 │ r0
0 │ o─┼───┘ └───┴───┴───┴───┘
├───┤ ┌───┬───┬───┬───┐
1 │ o─┼────────>│ 5 │ 6 │ 7 │ 8 │ r1
├───┤ └───┴───┴───┴───┘
2 │ o─┼───┐ ┌───┬───┬───┬───┐
└───┘ └────>│ 9 │ 0 │ 1 │ 2 │ r2
└───┴───┴───┴───┘
/**
* Matriisi kasattuna irrallisista riveistä
* @author Vesa Lappalainen
* @version 1.0, 04.03.2003
*/
public class Mat3 {
public static double alkioiden_summa(double mat[][],int riveja, int sarakkeita) {
int riv = Math.min(riveja,mat.length);
double summa = 0;
for (int i=0; i<riv; i++) {
int sar = Math.min(sarakkeita,mat[i].length);
for (int j=0; j<sar; j++)
summa += mat[i][j];
}
return summa;
}
public static void main(String[] args) {
double s1,s2;
double r0[] = {1,2,3,4}, r1[] = {5,6,7,8}, r2[] = {9,0,1,2};
double mat[][] = {r0,r1,r2};
s1 = alkioiden_summa(mat,2,3);
s2 = alkioiden_summa(mat,3,4);
System.out.println("Summat on " + s1 + " ja " + s2);
}
}
Javan menettelyssä on vielä se etu, ettei kaikkien rivien välttämättä tarvitsisi edes olla yhtä pitkiä. Harvassa matriisissa osa osoittimista voisi olla jopa null–osoittimia, mikäli rivillä ei ole alkioita (aliohjelman pitäisi tietysti tarkistaa tämä). Oikeasti rivit usein vielä luotaisiin dynaamisesti ajonaikana tarvittavan pituisina.
Tehtävä 13.4 Transpoosi
Kirjoita taulukko–osoittimia käyttäen aliohjelma, joka saa parametrinään kaksi matriisia ja niiden dimensiot. Aliohjelma tarkistaa voiko toiseen matriisiin tehdä toisen transpoosin (vaihtaa rivit ja sarakkeet keskenään) ja tekee transpoosin jos pystyy. Onnistuminen palautetaan aliohjelman nimessä.
Esimerkiksi Java–kielinen pääohjelma saa käyttöjärjestelmältä tällaisen taulukon kutsussa olleista argumenteista:
/**
* Ohjelma tulostaa komentorivin parametrit
* @author Vesa Lappalainen
* @version 1.0, 04.03.2003
*/
public class Argv {
public static void main(String[] args) {
System.out.println("Argumenttejä on " + args.length + " kappaletta:");
for (int i=0; i<args.length; i++)
System.out.println(i + ": " + args[i]);
}
}
Kun ohjelma ajettaisiin komentoriviltä saattaisi tulostus olla seuraavan näköinen (MS–DOS -koneessa):
C:\kurssit\moniste\esim\java-taul>java Argv kissa istuu puussa[RET]
Argumentteja on 3 kappaletta:
0: kissa
1: istuu
2: puussa
C:\kurssit\moniste\esim\java-taul>_
argvs┐ ┌───┬───┬───┬───┬───┬
│ ┌────────>│ k │ i │ s │ s │ a │
v │ └───┴───┴───┴───┴───┴
┌───┐ │ ┌───┬───┬───┬───┬───┐
0 │ o─┼─┘ ┌──────>│ i │ s │ t │ u │ u │
├───┤ │ └───┴───┴───┴───┴───┘
1 │ o─┼───┘ ┌───┬───┬───┬───┬───┬───┐
├───┤ ┌────>│ p │ u │ u │ s │ s │ a │
2 │ o─┼─────┘ └───┴───┴───┴───┴───┴───┘
└───┘
Tehtävä 13.5 Palindromi
Kirjoita Java-ohjelma Pali, jota kutsutaan komentoriviltä seuraavasti:
C:\OMAT\OHJELMOI\VESA>java Pali kissa[RET]
kissa EI ole palindromi!
C:\OMAT\OHJELMOI\VESA>java Pali saippuakauppias[RET]
saippuakauppias ON palindromi!
C:\OMAT\OHJELMOI\VESA>_