Kurssilla opittiin jo kauan sitten, miksi taulukot ovat hyödyllisiä ja käyttökelpoisia: yksi muuttuja tarkoittaa kokonaista "lokerikkoa", jossa kukin "lokero" käyttäytyy kuin yksittäinen muuttuja. Ja oleelliseksi osoittautui, että taulukkoa voidaan indeksoida dynaamisesti indeksin arvoa muuttelemalla:
String[] nimet = new String[3]; nimet[0] = "Aku"; nimet[1] = "Iines"; nimet[2] = "Hessu"; for (int indeksi=0; < nimet.length; ++) System.out.println(nimet[]);
Edellä opittiin myös näppärä "for-each"-toisto, jota voi käyttää, ellei taulukon alkioiden arvoja muuteta eikä tarvita tietoa indekseistä:
String[] nimet = new String[3]; nimet[0] = "Aku"; nimet[1] = "Iines"; nimet[2] = "Hessu"; for (String alkio: nimet) System.out.println(alkio);
Tässä luvussa opitaan lisää taulukoiden ominaisuuksia, käyttöä ja erityisesti muutamia keskeisiä ohjelmointitekniikoita: etsimistä ja järjestämistä.
Javassa alkeistyyppejä ovat vain numeeriset tyypit (int, double, char, ...) ja totuusarvotyyppi (boolean). Kaikki muut tyypit ovat viittaustyyppejä. Javassa kaikki viittaustyypin arvot ovat olioita, luokan ilmentymiä. Ja kaikki oliot "roikkuvat aina langan päässä", eli kun muuttujan arvona on olio, muuttujan arvona on tarkemmin sanoen aina viite olioon.
Taulukko ei ole poikkeus: Javassa taulukko on olio ja siis taulukkomuuttuja viittaa taulukko-olioon.
Tarkastellaan aluksi pientä yksinkertaista esimerkkiä ja selvennetään käsitteitä:
int[] luku = new int[10];
luku
ja
ilmaisee, että tämän muuttujan arvoksi voidaan asettaa milloin mikin
int[]-tyyppinen taulukko-olio, mutta ei
koskaan mitään muuta. Muuttujan arvoksi voidaan siis ohjelman suorituksen
aikana asettaa viite eri kokoisiinn taulukko-oliohin,
mutta vain sellaisiin, joiden alkiot ovat tyyppiä int
.
[Huom: Javassa on valitettavasti mahdollista määritellä
samaa tarkoittaen myös
int luku[] = new int[10];
Tämä onneton kirjoitustyyli 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ä:
.length
.
Jos muuttujan luku
arvona on 10-alkionen taulukko-olio,
luku.length
on 10.
Seuraavat aika keinotekoiset ohjelmanpätkät antavat esimerkkejä monenlaisista taulukkomuuttujien määrittelyistä, taulukko-olioiden konstruoinnista ja taulukoiden käytöstä:
// int-taulukoita: "kokonailukutaulukkoA" jne. int[] kA; // taulukkomuuttuja, EI vielä taulukkoa! int[] kB = new int[4]; // muuttuja ja taulukko-olio int[] kC = {5, -23, 12, 31, 7}; // olio alkiot luetellen, luvallista vain // muuttujan määrittelyn yhteydessä for (int i=0; i<kB.length; ++i) // indeksoiden läpikäynti kB[i] = 2 * (i+1); for (int alkio: kB) // "for-each" System.out.println(alkio); kA = kC; // KOPIOIDAAN VIITE kA[1] = 123456; // MYÖS kC[1] MUUTTUU! System.out.println(kA[1] + " " + kC[1]); kC = new int[3]; // luodaan uusi taulukko kC:n arvoksi kC[1] = -98; System.out.println(kA[1] + " " + kC[1]);
Tulostus:
2 4 6 8 123456 123456 123456 -98
// double-taulukoita: "doubletaulukkoA" jne. double[] dA = new double[5]; double[] dB = {3.14, 7.21, 0.0, 9.1}; // siis double[4]! dA[2] = dB[0] + dB[3];
// boolean-taulukoita: "totuusarvotaulukkoA" jne. boolean[] tA = new boolean[4]; boolean[] tB = {true, true, false}; // siis boolean[3]! tA[1] = tB[0] || tB[2]; if (tA[1]) System.out.println("Tosi on!");
Tulostus:
Tosi on!
// char-taulukoita: "merkkitaulukkoA" jne. char[] mA = new char[5]; char[] mB = {'q', 'w', 'e'}; // siis char[3]! mB[1] = 'u'; System.out.println("Merkkialkion oletusalkuarvo on" + mA[2] + ".");
Tulostus:
Merkkialkion oletusalkuarvo on.
// String-taulukoita: "merkkijonotaulukkoA" jne. String[] jA = new String[3]; String[] jB = {"kissa", "hiiri"}; // siis String[2]! jA[0] = jB[1]; jA[2] = jB[0]; jA[1] = "koira"; for (String alkio : jA) System.out.println(alkio); jB[1] = jA[1];
Tulostus:
hiiri koira kissa
Tähän tutustuttiin jo aiemmin, mutta kertaus ei ole pahasta!
Tyypillinen ohjelmointitehtävä on sen selvittäminen, onko taulukossa jotakin kysyttyä alkiota. Jos taulukko ei ole järjestyksessä, ns. peräkkäishaku on normaali menettely.
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 monta, palautetaan niistä ensimmäisen indeksi). Jos kysyttyä ei löydy, metodi palauttaa arvon -1, joka ei koskaan voi kelvata taulukon indeksiksi:
public class EsimerkkiPerakkaishausta { // Peräkkäishaku int-taulukosta // halutaan indeksi //------------------------------------------------------------------------- private static int hae(int[] taulu, int haettava) { // int-metodi! for (int i=0; i<taulu.length; ++i) if (taulu[i] == haettava) return i; // tärppäsi, lopetetaan samantien // jos tähän päästiin, ei löytynyt! return -1; } //------------------------------------------------------------------------- public static void main(String[] args) { // esittelypääohjelma int[] a = {40, 20, 50, 10, 30}; System.out.println(hae(a, 10)); // tulostaa 3 System.out.println(hae(a, 20)); // tulostaa 1 System.out.println(hae(a, 30)); // tulostaa 4 System.out.println(hae(a, 40)); // tulostaa 0 System.out.println(hae(a, 50)); // tulostaa 2 System.out.println(hae(a, 60)); // tulostaa -1 } }
Jos riittää pelkkä tieto, löytyykö kysytty, peräkkäishaku voidaan ohjelmoida myös totuusarvoisena metodina:
public class Esimerkki2Perakkaishausta { // Peräkkäishaku int-taulukosta // halutaan vain tieto löytyykö //------------------------------------------------------------------------- private static boolean hae(int[] taulu, int haettava) { // boolean-metodi! for (int alkio: taulu) if (alkio == haettava) return true; // tärppäsi, lopetetaan samantien // jos tähän päästiin, ei löytynyt! return false; } //------------------------------------------------------------------------- public static void main(String[] args) { // esittelypääohjelma int[] a = {40, 20, 50, 10, 30}; System.out.println(hae(a, 10)); // tulostaa true if (hae(a, 20)) System.out.println("Löytyy!"); // tulostaa Löytyy! else System.out.println("Ei Löydy!"); System.out.println(hae(a, 60)); // tulostaa false } }
Jos taulukko on järjestyksessä, ns. binäärihaku on erittäin tehokas väline arvon hakemiseen taulukosta.
public class EsimerkkiBinaariHausta { // 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; } // jos tähän päästiin, hakualue tuli tyhjäksi eikä löytynyt! return -1; } //------------------------------------------------------------------------- public static void main(String[] args) { // esittelypääohjelma int[] a = {10, 20, 30, 40, 50}; System.out.println(binHae(a, 5)); // tulostus -1 System.out.println(binHae(a, 10)); // tulostus 0 System.out.println(binHae(a, 20)); // tulostus 1 System.out.println(binHae(a, 30)); // tulostus 2 System.out.println(binHae(a, 35)); // tulostus -1 System.out.println(binHae(a, 40)); // tulostus 3 System.out.println(binHae(a, 50)); // tulostus 4 System.out.println(binHae(a, 60)); // tulostus -1 } }
Taulukon alkioiden järjestäminen on myös usein tarvittu toimenpide. Järjestämisalgoritmeja on monenlaisia, tehokkaita ja vähemmän tehokkaita.
Kuplajärjestäminen voidaan ohjelmoida vaikka seuraavasti:
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]; // jos on, vaihdetaan! taulu[j] = taulu[j+1]; taulu[j+1] = apu; } }
Edellisiä selvästi parempi järjestämisalgoritmi on ns. lisäysjärjestäminen (insertion sort):
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 ns. "yleiset" (=vertailuun perustuvat) järjestämisalgoritmit "toimivat ajassa O(n*log n)".
Toisaalta suoritusaikoja voidaan myös kokeellisesti mitata. Pieni vertailu antoi seuraavia suoritusaikoja (3.10.2011) kun järjestettiin 100000-alkoinen int-taulukko (ohjelma VertaileJarjAlgoritmeja.java). Ajat riippuvat tietysti käytettävästä tietokoneesta, mutta suhteet säilynevät. Esimerkin ajat ovat millisekunteina.
arvottu valmiiksi käänteisessä alkujärjestys järjestetty järj. oleva taulukko taulukko vaihtojärj. 23947 ms. 12512 ms. 11476 ms. kuplajärj. 26046 ms. 13199 ms. 11836 ms. lisäysjärj. 1923 ms. 4 ms. 3847 ms.
Kuten taulukosta näkee, lisäysjärjestäminen on muita paljon nopeampi, vaikka se kuuluukin samaan "tehokkuusluokkaan" toisten kanssa. Ja erityisesti melkein järjestyksessä olevien taulukoiden järjestämisessä se on ylivoimainen.
Huom: Koska yllä annettu ohjelma VertaileJarjAlgoritmeja.java ei välttämättä toimi kunnolla kaikissa Windows-järjestelmissä, käytettävissä on myös versio, joka mittaa ajat nanosekunnin tarkkuudella: VertaileJarjAlgoritmejaB.java. Tämän version pitäisi Windowsissakin antaa vertailukelpoisia aikoja.
Kokeillaanpa sitäkin:
arvottu valmiiksi käänteisessä alkujärjestys järjestetty järj. oleva taulukko taulukko vaihtojärj. 23715.334903 ms. 12525.66967 ms. 11508.570413 ms. kuplajärj. 25989.121225 ms. 13181.302456 ms. 11857.060521 ms. lisäysjärj. 1919.676356 ms. 3.474728 ms. 3836.819151 ms.
Tällainen tarkkuus ei toki olisi tässä tarpeen...
Huom: Tällaisessa mittaamisessa on omat ongelmansa: Jos tietokone on vaikkapa kytkettynä verkkoon, ennakoimattomat tietoliikennetapahtumat voivat vääristää mittaustuloksia. Suorituskyvyn mittaaminen onkin tärkeä tietojenkäsittelytieteen osa-alue. Se ei ole helppoa!
Huom: (6.10.) Yksi kurssin opiskelija (DN) antoi mainion linkkivinkin: järjestämisalgoritmien visualisoija Sorting Visualizer. Pidemmälle ehtineet, huomatkaa että tuonne voi yksinkertaisella kielellä ohjelmoida myös omia järjestämisalgoritmeja visualisoitaviksi! Tässä esimerkkinä yksinkertainen vaihtojärjestäminen tuon sivun ohjelmointikielellä CoffeeScriptillä:
for i in [0...VA.length - 1] for j in [i + 1...VA.length] if VA.gt(i,j) VA.swap(i,j)
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ä kenttänä sillä on alkuarvo null.
Erityisesti on siis syytä huomata, että määrittely String[] taulu; EI siis luo taulukko-oliota, se vain nimeää muuttujan, joka voidaan asettaa viittaamaan taulukko-olioon. Taulukko-olio on siis luotava erikseen:
String[] taulu = new String[5];
asettaa muuttujan taulu
alkuarvoksi viisialkioisen
String-taulukko-olion.
Ja tässä on syytä huomata erityisesti, että määrittely
new String[5] luo taulukko-olion, jonka alkioilla
EI ole arvonaan vielä mitään String-olioita.
String-taulukon alkioilla on vain oletusalkuarvo
null, viite "ei mihinkään olioon"!
Lause
String[] taulu = {"kissa", "hiiri", "koira"};
asettaa muuttujan taulu alkuarvoksi kolmialkioisen String-taulukko-olion, jonka alkioilla on luetellut alkuarvot.
Huom: Pääohjelmametodin pakollinen parametri
String[] args
on siis String-taulukko. Sen alkuarvoksi tulevat automaattisesti ohjelman käynnistyskomentoa seuraavat välilyönnein toisistaan erotetut merkkijonot, ns. komentoriviparametrit. Jos ohjelma Ohjelma käynnistetään komennolla
java Ohjelma omena appelsiini luumu
pääohjelman args-parametri saa alkuarvokseen kolmialkioisen String-taulukon, jonka alkioilla on arvot "omena", "appelsiini" ja "luumu".
Esimerkkejä String-taulukoista:
public class StringTauluKokeilu { public static void main(String[] args) { if (args.length == 0) System.out.println("Ei komentoriviparametreja."); else for (String yksiParametreista: args) // komentoriviparametrit System.out.println(yksiParametreista); String[] ekaTaulu = new String[4]; // alkioiden oletusalkuarvo on null for (String yksiAlkioista: ekaTaulu) System.out.println(yksiAlkioista); for (int i=0; i < ekaTaulu.length; ++i) ekaTaulu[i] = "alkio numero " + i; // viedään sisältöä taulukkoon for (String yksiAlkioista: ekaTaulu) System.out.println(yksiAlkioista); String[] tokaTaulu = {"kissa", "hiiri", "koira"}; // annetaan alkuarvot for (String yksiAlkioista: tokaTaulu) System.out.println(yksiAlkioista); } }
Kun tämä ohjelma käynnistetään komennolla:
java StringTauluKokeilu omena appelsiini luumu
saadaan tulostus:
omena appelsiini luumu null null null null alkio numero 0 alkio numero 1 alkio numero 2 alkio numero 3 kissa hiiri koira
Kuten jo aiemmin on opittu, String-arvoja toki voi vertailla operaatioilla == ja !=. Tällaiset vertailut eivät kuitenkaan tutki merkkijonojen sisällön samuutta vaan vastaavat kysymykseen "ovatko oliot samat". Erisuuruusvertailut sen sijaan eivät ole lainkaan käytettävissä String-arvoille eivatkä itse asiassa millekään muillekaan viittaustyyppisille arvoille (eli olioille).
Aiemmin jo opittiin tärkeä String vertailujen väline:
Tällä välineellä String-taulukon voi järjestää "aakkosjärjestykseen" (itse asiassa hienosti sanottuna merkkikoodien mukaiseen positionaaliseen järjestykseen...) :
public class StringienVaihtoJarj { // Yksinkertainen vaihtojärjestäminen String-taulukolle //------------------------------------------------------------------------- private static void vaihtoJarjesta(String[] taulu) { for (int i=0; i < taulu.length-1; ++i) for (int j=i+1; j < taulu.length; ++j) if (taulu[i].compareTo(taulu[j]) > 0 ) { // onko järjestys väärä? String apu = taulu[i]; // jos on, vaihdetaan! taulu[i] = taulu[j]; taulu[j] = apu; } } //------------------------------------------------------------------------- public static void main(String[] args) { // esittelypääohjelma String[] a = {"omena", "appelsiini", "luumu", "kissa", "hiiri", "koira"}; 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(); } }
Tulostus:
omena appelsiini luumu kissa hiiri koira appelsiini hiiri kissa koira luumu omena
Huomaa miten vähän esimerkkiin tuli muutoksia int-versioon verrattuna! Jatkokurssilla nähdään, miten voidaan ohjelmoida esimerkiksi juuri järjestämisalgoritmeja, jotka osaavat järjestää mitä vain tietyn ehdon täyttäviä viittaustyyppisiä arvoja.
Merkkijonot eli String-oliot ovat kerran synnyttyään muuttumattomia.
Muuttuvien merkkijonojen "merkkipeliin" Javassa on tarjolla luokat StringBuilder ja StringBuffer. Ne jätetään kuitenkin tämän kurssin ulkopuolelle. Javan APIsta löytyy tarvittaessa tietoa.
Koska on kuitenkin hyödyllistä oppia tekemään asiat "omin käsin", tällä kurssilla 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 (char alkio : jono2) // for-each! System.out.print(alkio); 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 (char alkio : jono1) // for-each! System.out.print(alkio); System.out.println();
Lauseet tulostavat:
kissa *****assikString-olioita voi muuntaa char[]-taulukoiksi String-luokan metodilla:
public char[] toCharArray()
Merkkitaulukosta puolestaan voi luoda String-olion konstruktorilla:
public String(char[] value)
Laaditaan esimerkkiohjelma, joka lukee merkkijonon ja tulostaa sen sellaisena, jossa kaikki 'a'-merkit on korvattu merkillä 'ä' ja 'A'-merkit on korvattu merkillä 'Ä' (Aeta.java):
import java.util.Scanner; public class Aeta { private static Scanner lukija = new Scanner(System.in); public static void main(String[] args) { System.out.println("Anna merkkijono"); String jono = lukija.nextLine(); char[] mtaulu = jono.toCharArray(); for (int i=0; i<mtaulu.length; ++i) if (mtaulu[i] == 'a') mtaulu[i] = 'ä'; else 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 myös char-talukkoparametrille:
System.out.println(mtaulu);
Matriisi on tietojen esitys muodossa rivit-sarakkeet, esimerkiksi:
_0:__1:__2: 0: | 5 2 3 | 1: |_9__-1___4_|
Tässä matriisissa on kaksi riviä ja kolme saraketta.
Toisinaan matriisia kutsutaan myös kaksiulotteiseksi taulukoksi. Myös esimerkiksi kolmiulotteinen taulukko on mahdollinen, samoin neliulotteinen...
Javassa oikeastaan ei ole kaksiuluotteisia taulukoita, mutta koska Javan taulukoiden komponenttityyppi voi olla mikä tahansa Javan tyyppi, myös taulukkotyyppi voi olla taulukon komponenttina! Näin saadaan luotua rakenteita jotka muistuttavat moniulotteisia taulukoita. Tässä luvussa tutustutaan vain "kaksiulotteisiin" taulukoihin, matriiseihin. Ja niitä kutsutaan jatkossa kaksiulotteisiksi ilman sitaatteja.
Määrittely
int[][] matriisiA;
tarkoittaa, että muuttujan matriisiA tyyppi on int[]-taulukoista muodostuva taulukko. Muuttujan arvoksi siis voi asettaa viitteen tällaiseen olioon. Vielä sillä ei ole arvonaan tällaista viitettä.
Kun kirjoitetaan
int[][] matriisiA = new int[2][3];
muuttujan määrittelyn yhteydessä samalla luodaan muuttujan arvoksi kaksialkioinen taulukko, jonka kummatkin alkiot ovat viitteitä kolmialkoisiin int-taulukoihin. Tai sama toisin ajatellen: muuttujan arvoksi asetetaan 2*3 int-matriisi. Matriisin int-alkioiden alkuarvo on 0.
Matriisi voidaan myös luetella alkioiden alkuarvoina:
int[][] matriisiB = { {5, 2, -3}, {9, -1, 4} };
Huomaa miten tässä luetellaan kaksi yksiulotteista int-taulukkoa!
Myös "taulukoiden taulukkoa" indeksoidaan. Kun indeksoidaan yhdellä indeksillä
matriisiB[0]
tarkoitetaan taulukkomuuttujan ensimmäistä int[]-taulukkoa:
{5, 2, -3}
Kun tuosta halutaan valita vaikkapa toinen alkio, kirjoitetaan:
matriisiB[0][1]
Tällaista kahdella indeksillä indeksoitua matriisin alkiota käytetään sitten aivan kuin mitä tahansa int-muuttujaa:
matriisiB[0][1] = 66; matriisiB[2][1] = matriisiB[0][1] - 3; ...
Kaksiulotteisen taulukon alkiot voidaan läpikäydä for-lauseen sisältämällä for-lauseella seuraavaan tapaan:
for (int rivi=0; rivi<matriisi.length; ++rivi) for (int sarake=0; sarake<matriisi[rivi].length; ++sarake) // alialgoritmi alkion matriisi[rivi][sarake] käsittelyyn ...
Jos toistettavassa alialgoritmissa ei tarvita tietoa alkioiden indekseistä (riveistä ja sarakkeista) eikä käsittelyjärjestyksestä, läpikäynti voidaan ohjelmoida käyttäen "for-each"-toiston toistamaa "for-each"-toistoa:
for (int[] rivi : matriisi) for (int alkio : rivi) // alialgoritmi yhden alkion arvon käsittelyyn ...
Laaditaan esimerkkisovellus, joka lukee 2*3-matriisin ja tulostaa sen 7:llä kerrottuna (so. jokainen alkio kerrotaan 7:llä). Lopuksi ohjelma laskee ja tulostaa "7:llä kerrotun" matriisin alkioiden summan.
import java.util.Scanner; public class Matriisi7 { private static Scanner lukija = new Scanner(System.in); public static void main(String[] args) { int[][] matriisi = new int[2][3]; // 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] = lukija.nextInt(); } // 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(); } // lasketaan alkioiden summa: int summa = 0; for (int[] rivi : matriisi) // nyt tarvitaan vain alkioiden arvot! for (int alkio : rivi) summa = summa + alkio; System.out.println("Alkioiden summa on " + summa + "."); } }
Myös omia tyyppejä - eli omia luokkia - voidaan käyttää taulukon komponenttityyppinä! Esimerkiksi määrittely:
Varasto[] varastot = new Varasto[4];
määrittelee Varasto[]-tyyppisen muuttujan ja asettaa sen
arvoksi nelialkioisen taulukon, jonka kukin alkio
on tyypiltään Varasto
.
Ensimmäistäkään Varasto-oliota ei vielä synny.
Oliotaulukon alkioiden oletusalkuarvo on null.
Varasto-olioita voidaan sitten luoda erikseen tyyliin:
varastot[0] = new Varasto(100.0); varastot[1] = new Varasto(100.0, 20.2); varastot[2] = new Varasto(1027.8); varastot[3] = new Varasto(1027.8, 100.8);
Tämän jälkeen taulukon alkioita - noita luotuja Varasto-olioita - voidaan käyttää aivan samaan tapaan kuin aiemmin:
varastot[0].lisaaVarastoon(30.0); System.out.println(varastot[0].getSaldo()); varastot[0].otaVarastosta(7.0); System.out.println(varastot[0]); varastot[2] = varastot[0]; System.out.println(varastot[2]); // ...
Taulukkomuuttujan määrittelyn yhteydessä voidaan myös omien olioiden tapauksessa luoda taulukko-olio yksinkertaisesti luettelemalla alkuarvot:
Varasto[] varastot = {new Varasto(100.0), new Varasto(100.0, 20.2), new Varasto(1027.8), new Varasto(1027.8, 100.8)};
Kuten muitakin tyyppejä myös taulukkotyyppiä voi käyttää luokan kenttänä, ilmentymämuuttujana.
Toteutetaan esimerkkinä luokka Vakioveikkausrivi
veikkausrivin esittämiseen. API:
1x21X-----2x1
.
Toteutus:
public class Vakioveikkausrivi { // privaatti tietorakenne: private char[] rivi; // konstruktori: public Vakioveikkausrivi() { this.rivi = new char[13]; for (int i=0; i<rivi.length; ++i) rivi[i] = '-'; } // getteri: // kutsuja vastaa oikeasta kohteen valinnasta: 1-13 public char getArvaus(int moneskoKohde) { return rivi[moneskoKohde -1]; } // setterit: // kutsuja vastaa oikeasta kohteen valinnasta: 1-13 // jos ei 1, x, X tai 2, ei muutoksia public void setArvaus(int moneskoKohde, char arvaus) { if (arvaus=='1' || arvaus=='x' || arvaus=='X' || arvaus=='2') rivi[moneskoKohde -1] = arvaus; } // kuormitetaan: 1 ja 2 voidaan antaa myös int-arvona: public void setArvaus(int moneskoKohde, int arvaus) { if (arvaus==1) rivi[moneskoKohde -1] = '1'; else if (arvaus==2) rivi[moneskoKohde -1] = '2'; } public String toString() { return new String(rivi); } }
Käyttöesimerkki:
public class VakioveikkausrivinKokeilu { public static void main(String[] args) { Vakioveikkausrivi v = new Vakioveikkausrivi(); System.out.println("Tyhjä rivi: " +v); v.setArvaus(1, '1'); v.setArvaus(2, 'x'); v.setArvaus(3, '2'); v.setArvaus(4, 1); v.setArvaus(5, 'X'); v.setArvaus(6, 2); System.out.println("6 asetettu: " +v); v.setArvaus(13, '1'); v.setArvaus(12, 'x'); v.setArvaus(11, '2'); System.out.println("loppuun 3: " +v); } }
Esimerkkiohjelman tulostus:
Tyhjä rivi: ------------- 6 asetettu: 1x21X2------- loppuun 3: 1x21X2----2x1