Skip to main content Link Menu Expand (external link) Document Search Copy Copied

Taulukot

Tällä kerralla tutustumme Javan taulukoihin. Taulukot ovat varsin alkeellisia tietorakenteita, joihin voidaan varastoida useita saman typpisiä arvoja. Materiaalissa ja oppitunnilla oletetaan perustason osaamista listojen käytöstä.

Toisin kuin listoilla, taulukon pituus on kiinteä, eli niitä ei voi lyhentää eikä kasvattaa. Taulukoilla ei myöskään ole metodeita eikä samanlaista luonnollista merkkijonoesitystä kuin listoilla.

Listoilla ja taulukoilla on myös lukuisia yhtäläisyyksiä, ja ArrayList-listat jopa käyttävät sisäisesti taulukoita tietojensa tallentamiseen nimensä mukaisesti.


Sisällysluettelo

Video: taulukon luominen, käsitteleminen ja läpikäynti 40:29

Tällä videolla käsittelemme taulukoiden luomista sekä vertailemme niiden käyttämistä listojen käyttämiseen. Tutustumme taulukoiden luomiseen sekä tyhjinä että valmiilla arvoilla täytettyinä.

Videolla käsiteltävät lähdekooditiedostot:

Taulukot ja taulukkomuuttujat

Taulukkoja sisältävien muuttujien tyypiksi kirjoitetaan tallennettavan tyypin nimi ja sen jälkeen hakasulut, esim:

int[] numerot;
String[] sanat;
double[] liukuluvut;
boolean[] totuusarvot;

// myöhemmin kurssilla:
Yhteystieto[] yhteystiedot;
Auto[] autot;

Tässä vaiheessa kurssia pitäydymme Javan valmiissa tietotyypeissä kuten String emmekä vielä toteuta omia luokkia, kuten Yhteystieto tai Auto.

Taulukon luominen

Taulukot luodaan new-avainsanalla ja taulukon pituus määritellään hakasuluissa. Hakasulkujen sisään määritellään tällä syntaksilla taulukon pituus.

Pituutta ei voi enää muuttaa taulukon luomisen jälkeen.

String[] viikonpaivat = new String[7];  // 7 merkkijonoa
int[] lottonumerot = new int[7];        // 7 numeroa

Taulukon alkioihin viittaaminen

Taulukon alkioihin viitataan taulukon indeksien perusteella hakasulkujen avulla. Kuten listojen ja merkkijonojen tapauksessa, taulukoiden indeksit lasketaan seuraavasti:

  • ensimmäinen indeksi on 0
  • viimeinen indeksi on taulukon pituus - 1

Arvoja voidaan asettaa taulukkoon sijoitusoperaattorilla =. Tällöin sijoitusoperaattorin vasemmalle puolelle kirjoitetaan taulukon muuttuja ja haluttu indeksi:

viikonpaivat[0] = "maanantai";
viikonpaivat[1] = "tiistai";
// ...
viikonpaivat[6] = "sunnuntai";

Arvoja voidaan hakea taulukosta vastaavasti muuttujan ja indeksin avulla. Haettu arvo voidaan asettaa toiseen muuttujaan, tulostaa tai välittää eteenpäin, aivan kuten muutkin samantyyppiset arvot:

// arvon hakeminen indeksistä 3:
String torstai = viikonpaivat[3];

// arvon hakeminen ja tulostaminen:
System.out.println(viikonpaivat[3]);

// arvon hakeminen, muuttaminen isoksi ja tulostaminen:
System.out.println(viikonpaivat[3].toUpperCase());

Toisin kuin listoja, taulukkoa ei täydy täyttää alusta loppuun, vaan voit täyttää sen missä järjestyksessä tahansa. Tässä esimerkissä luodaan kolmepaikkainen kokonaislukutaulukko, jonka jälkeen taulukon indekseihin 0 ja 2 asetetaan arvot:

// Luodaan muuttuja ja taulukko:
int[] luvut = new int[3];

// Asetetaan taulukkoon arvoja:
luvut[0] = 2;
luvut[2] = 5;

// Arvojen lukeminen taulukosta:
System.out.println(luvut[0]);
int luku = luvut[2];

Katso Java Visualizerissa!

Tämä esimerkki on lainattu Helsingin yliopiston Agile Education Research –tutkimusryhmän oppimateriaalista, joka on lisensoitu Creative Commons BY-NC-SA-lisenssillä. https://2017-ohjelmointi.github.io/part6/

Taulukon tyhjät arvot

Koska taulukon pituus ei voi muuttua, on taulukossa heti luonnin jälkeen tarvittava määrä paikkoja. Oletusarvo numerotaulukoilla on nolla 0 ja boolean-taulukoilla false. Oliotaulukoilla (esim. merkkijonotaulukot), oletusarvo on tyhjä viittaus, eli null.

// taulukko täytetään oletusarvoilla, eli nollilla:
int[] luvut = new int[3];       // [0, 0, 0]

// viittaustyyppisten taulukoiden oletusarvo on `null`:
String[] sanat = new String[4]; // [null, null, null, null]

Taulukon luominen valmiilla arvoilla

Jos taulukkoon asetettavat alkuarvot ovat jo valmiiksi tiedossa, taulukko voidaan alustaa myös aaltosulkeiden avulla tietyille arvoille.

Tällöin taulukon pituutta ei ilmoiteta hakasuluissa, vaan pituus määräytyy alkuarvojen määrän mukaan, esim:

int[] lottorivi = new int[] { 3, 9, 15, 16, 26, 29, 33 };

Java osaa myös päätellä tässä tapauksessa taulukon tyypin, joten voimme kirjoittaa saman ilman new int[] -osaa:

int[] lottorivi = { 3, 9, 15, 16, 26, 29, 33 };

Taulukoiden alustaminen valmiiksi tiedossa olevilla merkkijonoilla toimii samalla tavalla:

String[] viikonpaivat = { "Ma", "Ti", "Ke", "To", "Pe", "La", "Su" };

Taulukon koko ja sen arvojen läpikäynti

Taulukon koon saa selville taulukkoon liittyvän muuttujan length avulla. length-muuttujaan pääsee käsiksi kirjoittamalla taulukon muuttujan nimen, pisteen sekä length-muuttujan nimen, eli esimerkiksi:

int[] luvut = new int[3];

int pituus = luvut.length;

Huomaa, että kyseessä ei ole metodikutsu kuten listoilla, eli taulukko.length() ei toimi.

Taulukon alkioiden läpikäynti voidaan toteuttaa esim. while- tai for-toistorakenteen avulla:

int[] luvut = new int[4];
luvut[0] = 42;
luvut[1] = 13;
luvut[2] = 12;
luvut[3] = 7;

System.out.println("Taulukossa on " + luvut.length + " alkiota.");

int indeksi = 0;
while (indeksi < luvut.length) {
    System.out.println(luvut[indeksi]);
    indeksi++;
}

Katso Java Visualizerissa!

Tämän esimerkki on lainattu Helsingin yliopiston Agile Education Research –tutkimusryhmän oppimateriaalista, joka on lisensoitu Creative Commons BY-NC-SA-lisenssillä. https://2017-ohjelmointi.github.io/part6/

Taulukon hyödyntämistä

Koska taulukot ovat jonkin verran listoja rajoittuneempi tietorakenne, voi olla, että päädyt itse luomaan taulukkoja harvemmin. Taulukot ovat kuitenkin hyödyllinen tietorakenne erityisesti silloin, kun tarvittava kokoelman koko on ennalta tiedossa. Myös Javan standardikirjastossa hyödynnetään taulukoita, eli vaikka et itse loisi omia taulukoita, tarvitset perustaidot taulukoiden hyödyntämisestä esimerkiksi pilkkoessasi merkkijonoja.

Merkkijonon split-metodi

Merkkijonoilla (String-luokka) on split-niminen metodi, jolla merkkijono voidaan pilkkoa osiin tietyn merkin tai osamerkkijonon kohdalta. Split palauttaa merkkijonotaulukon, jossa on alkuperäisen merkkijonon osat ilman pilkkomisessa käytettyjä merkkejä.

String teksti = "ma ti ke to pe la su";

// pilkotaan välilyöntien kohdalta
String[] paivat = teksti.split(" ");

int pituus = paivat.length;
System.out.println(pituus); // 7

System.out.println(paivat[0]); // ma
System.out.println(paivat[1]); // ti
System.out.println(paivat[2]); // ke

System.out.println(paivat[pituus - 1]); // su

Pilkkominen voi olla hyödyllistä esimerkiksi koneellisesti luettavaksi tarkoitettujen esitysmuotojen, kuten päivämäärien tai CSV-tiedostojen, lukemisessa:

String ajanhetki = "2020-9-19T7:9:18";

String[] osat = ajanhetki.split("T");

System.out.println(osat[0]); // "2020-9-19"
System.out.println(osat[1]); // "7:9:18"

String paivamaara = osat[0];
String[] pvmOsat = paivamaara.split("-");
System.out.println(Arrays.toString(pvmOsat)); // ["2020", "9", "19"]

String kellonaika = osat[1];
String[] kelloOsat = kellonaika.split(":");
System.out.println(Arrays.toString(kelloOsat)); // ["7", "9", "18"]

CSV-tiedostot (comma-separated values)

Taulukkomuotoisen tiedon tallentamiseen yksinkertaisina tekstitiedostoina käytetään usein CSV-tiedostoja:

“CSV on toteutukseltaan tekstitiedosto, jonka taulukkorakenteen eri kentät on eroteltu toisistaan pilkuilla ja rivinvaihdoilla. Jos jokin kenttä sisältää erikoismerkkejä, kyseinen kenttä ympäröidään pystysuorilla lainausmerkeillä (“). Ensimmäisellä rivillä voi olla kenttien selitykset samassa muodossa kuin mitä itse tiedot ovat.”

Wikipedia. CSV. https://fi.wikipedia.org/wiki/CSV

Wikipedian esimerkissä autojen tiedot on esitetty tallennettuna seuraavassa CSV-muodossa:

Vuosi,Merkki,Malli,Pituus
1997,Ford,E350,2.34
2000,Mercury,Cougar,2.38

Sama data on esitettävissä myös taulukkomuodossa:

Vuosi Merkki Malli Pituus
1997 Ford E350 2.34
2000 Mercury Cougar 2.38

Koska CSV-tiedostot on helposti koneluettavia ja -kirjoitettavia, hyvin monet ohjelmat tukevat niitä tiedon tallennusmuotonaan. Myös CSV-tiedostojen käsittelyssä split-metodista on hyötyä, vaikkakin monimutkaisempia CSV-rakenteita kannattaa lukea erillisten kirjastojen avulla.

Säätilasto-esimerkki (csv-datan käsittely) 44:17

Tunnilla kirjoitimme ohjelman, joka lukee Ilmatieteen laitoksen avointa csv-muotoista säädataa ja kertoo aineiston lämpimmän ja kylmimmän havainnon päivämäärineen.

Videolla esiintyvä lähdekooditiedosto löytyy GitHubista: LampotilaTilasto.java

Voit tallentaa videolla esiintyvän säädatan itsellesi csv-muodossa osoitteesta https://www.ilmatieteenlaitos.fi/havaintojen-lataus. Aineisto on lisensoitu Creative Commons Nimeä 4.0 -lisenssillä.

For each –toistokäsky ja listojen sekä taulukoiden läpikäynti

Taulukon kaikki alkiot voidaan käydä läpi for each -toistokäskyllä, aivan kuten listojen alkiot. Kertaa tarvittaessa for each -rakenne (advanced for loop) lista-aiheen muistiinpanoista tai Googlen avulla.

int[] luvut = {2, 4, 8, 16, 32, 64};

for (int luku : luvut) {
    System.out.println(luku);
}

Vertailu: taulukoiden ja listojen eroja

Seuraavissa kahdessa esimerkkikoodissa luodaan merkkijonotaulukko sekä merkkijonolista, joihin molempiin asetetaan yksi arvo:

// Luodaan 10 merkkijonon pituinen taulukko:
String[] taulukko = new String[10];

// Lisätään sana taulukkoon:
taulukko[0] = "taulukkoon";

System.out.println(taulukko[0]);

System.out.println(taulukko.length); // 10
// Luodaan tyhjä merkkijonolista:
List<String> lista = new ArrayList<>();

// Lisätään sana listaan
lista.add("listalle");

System.out.println(lista.get(0));

System.out.println(lista.size()); // 1

Listojen ja taulukoiden toiminta on monilta osin samankaltaista. Taulukon pituus on kuitenkin muuttumaton, joten sen pituus on 10, vaikka sinne onkin lisätty vasta yksi merkkijono. Muut taulukon indeksit ovat vielä tyhjiä (null). Listan pituus kasvaa dynaamisesti, joten esimerkissä listan pituus on 1.

Taulukot Listat
Taulukon pituus määrätään sitä luotaessa Listan pituus kasvaa alkioita lisättäessä
Taulukon alkioita käsitellään hakasulkujen avulla Listan alkioita käsitellään metodien avulla
Taulukon pituus on aina kiinteä Lista luodaan tyhjänä ja se kasvaa rajattomasti
Taulukko voidaan täyttää missä vain järjestyksessä Listan täyttäminen alkaa aina indeksistä 0
Taulukkoon ei voida lisätä arvoja väleihin Listalle voidaan lisätä arvoja väleihin

Taulukon järjestäminen

Taulukon kaikki alkiot voidaan järjestää kasvavaan järjestykseen seuraavasti:

Arrays.sort(numeroTaulukko);

Vertaa listojen järjestäminen:

Collections.sort(numeroLista);

Taulukon tulostaminen

Mitä tapahtuu kun taulukko tulostetaan?

String[] kirjaimet = new String[] { "j", "a", "v", "a" };

Arrays.sort(kirjaimet);

// tulostaa hämmentävän merkkijonon:
System.out.println(kirjaimet); // [Ljava.lang.String;@106d69c

Taulukoilla ei ole oletuksena selkeää merkkijonoesitystä.

Arrays-apuluokasta löytyy kuitenkin staattinen metodi Arrays.toString(taulukko) merkkijonoesityksen muodostamiseksi. toString muodostaa merkkijonon, jonka voimme ottaa talteen muuttujaan tai antaa suoraan esim. println-metodille:

import java.util.Arrays; // alkuun tämä
String[] nimet = { "Superman", "Batman", "Chuck Norris" };

Arrays.sort(nimet);

String merkkijonoksi = Arrays.toString(nimet);

System.out.println(merkkijonoksi); // [Batman, Chuck Norris, Superman]

Viittaustyyppiset muuttujat käytännössä

Taulukoita käytetään Javassa viittaustyyppisillä muuttujilla, aivan kuten listoja, merkkijonoja ja muita olioita. Tämä tarkoittaa käytännössä sitä, että kun taulukko asetetaan muuttujaan tai välitetään parametrina toisaalle, taulukkoa ei kopioida, vaan samaan taulukkoon viitataan useammasta paikasta:

String[] sanat = new String[10];
String[] verbit = sanat; // ei kopioi taulukkoa

Ilmiötä havainnollistetaan seuraavassa koodiesimerkissä, jossa nimet ja lyhennetty ovat täsmälleen sama taulukko:

String[] nimet = { "Johan", "Ludvig", "Runeberg" };
String merkkijono = Arrays.toString(nimet);
System.out.println(merkkijono);

String etunimi = nimet[0]; // "Johan"
String toinenNimi = nimet[1]; // "Ludvig"
String sukunimi = nimet[2]; // "Runeberg"

// EI KOPIOI TAULUKKOA, VAAN VIITTAA SAMAAN TAULUKKOON:
String[] lyhennetty = nimet;

lyhennetty[0] = etunimi.substring(0, 1); // "J"
lyhennetty[1] = toinenNimi.substring(0, 1); // "L"

// Lyhennetty taulukko sisältää muuttuneet merkkijonot
System.out.println(Arrays.toString(lyhennetty));

// Myös alkuperäisen muuttujan kautta sisältö on muuttunut
System.out.println(Arrays.toString(nimet));

Voit tutustua koodin suoritukseen vaiheittain Java Visualizer -työkalulla:

Syventävää tietoa: taulukon kopioiminen

Taulukkoa ei ole mahdollista lyhentää tai pidentää, mutta siitä voidaan luoda eri pituinen kopio:

  • jos kopio on alkuperäistä taulukkoa lyhyempi, jää arvoja pois
  • jos kopio on alkuperäistä pidempi, täytetään loppuosa oletusarvoilla (null, 0 jne).

Monet operaatiot, kuten taulukon järjestäminen, muuttavat alkuperäistä taulukkoa pysyvästi. Usein alkuperäinen data halutaan pitää muuttumattomana, jolloin operaatioita tehdään taulukon kopiolle.

String[] kirjaimet = new String[] { "j", "a", "v", "a" };

String[] kopio = Arrays.copyOf(kirjaimet, kirjaimet.length);

String[] alkuosa = Arrays.copyOf(kirjaimet, 2); // [j, a]

Syventävää tietoa: main-metodin args-taulukko

Main-metodin otsikossa esiintyvä String[] args on merkkijonotaulukko, joka sisältää ohjelmalle annetut komentoriviparametrit. Eclipsessä suoritettaessa ne ovat tyhjät, mutta komentorivisovelluksissa tarvitsemme tätä taulukkoa.

import java.util.Arrays;

public class ArgsTaulukonTulostaminen {

    public static void main(String[] args) {
        // args on merkkijonotaulukko
        System.out.println(args.length);
        System.out.println(Arrays.toString(args));
    }
}

Yhteenveto: Arrays-apuluokka

Ote hyödyllisistä apumetodeista taulukoiden käyttöön:

Arrays.copyOf(original, int newLength)

Copies the specified array, truncating or padding (if necessary) so the copy has the specified length.

Arrays.toString(array)

Returns a string representation of the contents of the specified array.

Arrays.sort(array)

Sorts the specified array into ascending order.

Arrays.asList(array)

Returns a fixed-size list backed by the specified array. (Changes to the returned list “write through” to the array.) This method acts as bridge between array-based and collection-based APIs, in combination with Collection.toArray().

Lähde: https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html

Video: Lottotarkistin (sisäkkäiset toistorakenteet ja useita taulukoita) 33:49

Tällä videolla jatketaan aikaisemmalla videolla aloitettua lottoesimerkkiä ja kehitetään ohjelma, joka tarkistaa käyttäjän syöttämältä lottoriviltä siinä olevat oikeat numerot. Esimerkissä hyödynnetään useita taulukoita sekä sisäkkäisiä toistorakenteita. Lisäksi tustustumme Javan enhanced for loop -toistolauseeseen, jonka avulla taulukon arvot saadaan käytyä läpi suoraviivaisesti.

Lähdekoodit: Lottotarkistin.java.