Tehtävät

Lyhyt aloituskysely

Muutama hyödyllinen vinkki ohjelmointiin NetBeansissa

  • Vältä punaista!

    Kun NetBeans alleviivaa yhden tai useamman rivin punaisella värillä ja lähdekoodi-ikkunan vasemmassa ja oikeassa laidassa on punaista, NetBeans kertoo että ohjelmasi ei ole toimivassa tilassa. Ohjelmaasi ei voi myöskään tällöin suorittaa.

    Kuva NetBeans-editorista, missä koodi on kääntymättömässä tilassa.

    Ylläolevassa kuvassa NetBeans korostaa kahta virheellistä kohtaa. Ensimmäisessä kohdassa ehtolauseen ehdosta puuttuu mm. sulut. Toisessa kohdassa yritetään käyttää muuttujaa nimeltä luk, vaikkei sellaista ole.

    Kun punaisten kohtien määrä kasvaa, etkä saa ohjelmaasi toimimaan kuten haluat, pysähdy. Poista tekemiäsi muutoksia (ctrl + z) kunnes ohjelmointiympäristö ei valita lähdekoodissa olevista virheistä.

    Kun virheitä ei näy, hengitä ja kerro ääneen mitä haluat tehdä. Mikä on tavoitteesi ja minkälaisia pienempiä tavoitteita tavoitteesi saavuttamiseksi tulee tehdä? Etene pienin askelin ja toteuta ohjelmaasi osa kerrallaan. Ennen pitkää pienet osat tulevat sinulle tutuksi, ja opit yhä paremmin yhdistelemään niitä kokonaisiksi ohjelmiksi.

    Kuva NetBeans-editorista, missä koodi kääntyy.

    Tehtävien uudelleen alusta aloittamisessa ei ole mitään pahaa. Päinvastoin, kertaat tällöin ohjelman rakenteen toteuttamista, ja annat itsellesi aikaa oppia. Ohjelmakoodin lisääminen toimimattomaan ohjelmakoodiin johtaa harvoin toimivaan ratkaisuun.

  • Opettele hyödyntämään näppärää aaltosulkujen täydennystä

    Eräs aloittelevan ohjelmoijan tyypillinen syy "punaisen koodin" lisääntymiselle on aaltosulkujen liikakäyttö tai niiden liian vähäinen käyttö. Jokaiselle aaltosululle tulee löytyä ohjelmasta pari, sillä ne määrittelevät lohkoja, joissa ohjelmakoodia suoritetaan.

    NetBeans auttaa tässä huomattavasti. Kun kirjoitat ehtolauseen, siihen liittyvän ehdon, sekä avaavan aaltosulkeen, koodi näyttää esimerkiksi seuraavalta:

    Kuva NetBeans-editorista, missä yksi ylimääräinen avonainen aaltosulku.

    Koodi ei käänny -- NetBeans vinkkaa, että virhe on viimeisellä rivillä. Syy on kuitenkin puuttuva aaltosulku.

    Jos painat heti avaavan aaltosulkeen kirjoittamisen jälkeen enteriä, NetBeans luo kirjoittamallesi aaltosululle parin.

    Kuva NetBeans-editorista, missä yksi ylimääräinen avonainen aaltosulku.

    Punaista ei näy ja voit edetä rauhassa.

  • Opettele käyttämään koodin automaattista täydennystä

    Jos olet esitellyt ohjelmassasi muuttujan, ei sen nimeä tarvitse kirjoittaa joka kerta kokonaan.

    Kokeile mitä tapahtuu kun kirjoitat muuttujan ensimmäisen kirjaimen ja painat sen jälkeen yhtäaikaa ctrl ja välilyönti. HUOM: joissakin käyttöjärjestelmissä automaattinen täydennys saadaan aikaan painamalla yhtä aikaa ctrl, alt ja välilyönti.

    Allaolevassa esimerkissä ohjelmoija on luonut muuttujan sukunimi, jonka jälkeen hän kirjoittaa merkin s ja valitsee näppäinyhdistelmällä automaattisen täydennyksen.

    Kuva NetBeans-editorista, demonstroidaan automaattista koodin täydennystä.

    Muuttujan sukunimi voi valita NetBeansin tarjoamasta listasta. NetBeans osaa täydentää muitakin nimiä sekä avainsanoja, esimerkiksi muuttujan tyypin double aikaansaamiseksi riittää kirjoittaa w sekä valita automaattinen täydennys.

  • Käytä lyhennettä sout

    Muista että saat tehtyä tulostuskomennon System.out.println("") kirjoittamalla sout ja painamalla tabulaattoria eli q:n vasemmalla puolella olevaa näppäintä.

Toisen osion tavoitteet

Toisessa osiossa kerrataan ensimmäisen osion sisältöä: muuttujia sekä ehtolauseita. Uusina teemoina tulevat toistolause ja listarakenne.

Tehtävissä tulee olemaan paljon kertausta ja toistoa, joiden kautta harjoitellaan useampia muuttujia sisältävien ohjelmien ymmärtämistä.

Suosittelemme, että et kopioi (copy-paste) materialissa olevia esimerkkejä tai aiemmin tekemiesi tehtävien sisältöjä tehtäviisi pohjiksi, vaikka se on välillä mahdollista. Kopioimalla osia kurssilla harjoiteltavat rakenteet eivät jää samalla tavalla muistiin kuin jos kirjoittaisit niitä käsin, ja samalla miettisit niiden tarkoitusta.

Muuttuvat muuttujat

Ensimmäisen osion oleellisimmat asiat liittyivät muuttujiin, syötteen lukemiseen sekä ehtolauseiden käyttöön. Jatketaan samalla teemalla ja kerrataan samalla hieman.

Muuttujia käytetään tiedon säilyttämiseen sekä tiedon myöhempään noutamiseen. Muuttujan esittely tapahtui kertomalla ensin muuttujan tyyppi, esimerkiksi kokonaisluku (int), jota seuraa muuttujan nimi, esimerkiksi luku -- yhdessä int luku. Muuttujan esittelyä seuraa tyypillisesti sijoitusoperaatio, minkä avulla muuttujalle asetetaan arvo. Esimerkiksi lause int luku = 5; luo kokonaislukumuuttujan nimeltä luku ja asettaa siihen arvon 5.

Olemassaolevan muuttujan arvoa voi käyttää uuden muuttujan esittelyssä ja arvon asetuksessa. Alla olevassa koodissa luodaan ensin muuttuja nimeltä pituus, jonka jälkeen luodaan muuttuja paino. Muuttujaan paino asetetaan lausekkeen pituus / 2 evaluaation tulos, eli puolet muuttujan pituus arvosta.

int pituus = 166;
int paino = pituus / 2;

System.out.println(paino);
83

Olemassaolevan muuttujan arvoa voidaan muuttaa. Tämä onnistuu sijoituslauseen avulla. Seuraavassa esitellään ensin muuttuja ika, jolle asetetaan alkuarvoksi 1. Tämän jälkeen muuttujan ika arvoa kasvatetaan yhdellä:

int ika = 1;

System.out.println(ika);
ika = ika + 1;    // muuttujan ika uusi arvo on ika-muuttujan vanha arvo plus yksi
System.out.println(ika);
1
2

Tarkastellaan lausetta ika = ika + 1; tarkemmin. Lause ika = ika + 1; kasvattaa muuttujan ika arvoa yhdellä. Käytännössä komennon suoritus tapahtuu siten, että ensin suoritetaan yhtäsuuruusmerkin oikealla puolella oleva lauseke ika + 1. Tässä tietokone etsii ensin muuttujaa ika. Koska muuttuja on esitelty aiemmin, se löytyy (sen arvo on 1). Lausekkeen ika + 1 muuttujan ika paikalle asetetaan arvo 1. Lauseke on nyt muotoa 1 + 1, jonka tietokone osaa laskea: tulokseksi tulee 2. Lause on tämän jälkeen kokonaisuudessaan ika = 2, mikä tarkoittaa "aseta muuttujan ika arvoksi 2".

Muuttujan arvon kasvattaminen yhdellä onnistuu myös seuraavasti:

int ika = 1;

System.out.println(ika);
ika++;                     // tarkoittaa samaa kuin ika = ika + 1;
System.out.println(ika);

Lause ika++ muuntuu ohjelmaa suoritettaessa muotoon ika = ika + 1;. Ohjelman tulostus on sama kuin edellisessä:

1
2

Toinen esimerkki:

int pituus = 100;

System.out.println(pituus);
pituus = pituus - 50;
System.out.println(pituus);
pituus = pituus * 2;
System.out.println(pituus);
pituus = pituus / 4;
System.out.println(pituus);
pituus--;                   // sama kuin pituus = pituus - 1;
System.out.println(pituus);
100
50
100
25
24

Syötteen lukeminen

Käyttäjän kirjoittamaa syötettä voidaan lukea Scanner-apuvälineen avulla:

Scanner lukija = new Scanner(System.in);
int pituus = 100;

System.out.println(pituus);

System.out.print("Paljonko poistetaan? ");
pituus = pituus - Integer.parseInt(lukija.nextLine());
System.out.println(pituus);

System.out.print("Paljonko lisätään? ");
pituus = pituus + Integer.parseInt(lukija.nextLine());
System.out.println(pituus);

Tulostus esimerkiksi:

100
Paljonko poistetaan? 42
58
Paljonko lisätään? 1
59

Tee ohjelma, joka kysyy käyttäjältä kolme lukua ja tulostaa niiden summan. Huom! Käytä tehtäväpohjaan valmiiksi luotuja muuttujia -- älä siis luo uusia muuttujia koodiin. Tee ohjelmastasi seuraavan muotoinen:

Scanner lukija = new Scanner(System.in);
int summa = 0;

// KIRJOITA OHJELMA TÄHÄN
// ÄLÄ KÄYTÄ MUITA MUUTTUJIA KUIN lukija JA summa!

System.out.println("Summa: " + summa);
Anna ensimmäinen luku: 3
Anna toinen luku: 6
Anna kolmas luku: 12

Summa: 21

Tarkastellaan lauseen pituus = pituus - Integer.parseInt(lukija.nextLine()); suoritusta tarkemmin.

Kyseessä on sijoituslause, missä muuttujaan pituus asetetaan lausekkeen pituus - Integer.parseInt(lukija.nextLine()) evaluaation tuottama arvo. Lauseke pituus - Integer.parseInt(lukija.nextLine()) evaluoidaan seuraavasti:

  1. Tietokone etsii muuttujan pituus arvon. Koska muuttuja on luotu aiemmin, se löytyy: sen arvo on 100. Nyt lauseke on muotoa 100 - Integer.parseInt(lukija.nextLine());.
  2. Tietokone suorittaa lausekkeen osan Integer.parseInt(lukija.nextLine()):
    1. Ensin suoritetaan sulkujen sisällä oleva osa, eli lukija.nextLine(). Tämä johtaa siihen, että ohjelma jää odottamaan käyttäjältä syötettä. Kun käyttäjä antaa syötteen, esimerkiksi "42", vaihtuu komennon lukija.nextLine() paikalle käyttäjän syöttämä merkkijono "42". Seuraavaksi suoritettava komento on nyt Integer.parseInt("42");.
    2. Integer.parseInt("42") muuntaa tekstimuuttujan, jonka arvo on "42", kokonaislukumuuttujaksi, jonka arvo on 42.
  3. Lausekkeen Integer.parseInt(lukija.nextLine()) paikalle asetetaan sen evaluaatiosta saatu arvo, tässä 42. Nyt lauseke on muotoa 100 - 42.
  4. Tietokone evaluoi lausekkeen 100 - 42, jonka tulos on 58.

Lopulta lause on muotoa pituus = 58, eli muuttujan pituus arvoksi asetetaan 58.

Sijoitusoperaatiot

Olemassaolevan muuttujan arvon muuttaminen on hyvin yleinen operaatio, joten sitä varten on olemassa myös muutamia näppäinpainalluksia säästävät erityiset sijoitusoperaatiot.

int pituus = 100;

pituus += 10;  // sama kuin pituus = pituus + 10;
pituus -= 50;  // sama kuin pituus = pituus - 50;

Olemassaolevan muuttujan arvoa muuttava sijoitusoperaatio merkitään muuttuja muutostyyppi= muutos, esimerkiksi muuttuja += 5. Huomaa, että muuttujan tulee olla olemassa ennen kuin sille voidaan asettaa arvo. Muuttuja tulee siis aina esitellä ennen kuin se on käytettävissä. Muuttujan esittely tapahtuu kertomalla muuttujan tyyppi ja nimi.

Seuraava esimerkki ei toimi, sillä muuttujaa leveys ei ole esitelty.

leveys += 100;           // ei toimi!

Kun muuttuja on esitelty, laskukin toimii oikein.

int leveys = 100;  // muuttujan esittely ja arvon asetus
leveys += 100;     // toimii!

Myös muille kuin yhteen- ja vähennyslaskuille on Javassa vastaavat sijoitusoperaatiot.

int pituus = 100;
System.out.println(pituus);

pituus *= 10; // sama kuin pituus = pituus * 10;
System.out.println(pituus);

pituus /= 100; // sama kuin pituus = pituus / 100;
System.out.println(pituus);

pituus %= 3; // sama kuin pituus = pituus % 3;
System.out.println(pituus);
100
1000
10
1

Muuttujan olemassaolo

Muuttujan esittely ei takaa sen olemassaoloa kaikkialla ohjelmassa. Muuttujan olemassaoloon liittyy oleellisesti kaksi sääntöä: muuttujaa ei ole olemassa ennen kuin se on esitelty, ja muuttuja ei ole olemassa aaltosulkeidensa ulkopuolella.

Muuttujaa ei ole olemassa ennen sen esittelyä, eikä sitä voi käyttää ennen kuin se on esitelty.

// ei toimi sillä muuttujaa luku ei vielä tunneta!
if (luku > 10) {
    System.out.println("Jee!");
}

int luku = 12;
int luku = 12;

// toimii, sillä muuttuja esitellään ennen sen käyttöä
if (luku > 10) {
    System.out.println("Jee!");
}
Jee!

Muuttuja ei ole olemassa aaltosulkeidensa ulkopuolella, eikä siihen siis pääse käsiksi aaltosulkujen ulkopuolelta. Esimerkiksi, jos ehtolauseeseen liittyvässä lohkossa (aaltosuluilla rajattu alue) esitellään muuttuja, on kyseinen muuttuja on käytössä vain kyseisen lohkon sisällä.

int luku = 12;

if (luku > 10) {
    System.out.println("Jee!");
    int tulostus = 52;
}

// ei toimi sillä tulostus on olemassa vain ehtolauseen lohkojen sisällä
System.out.println(tulostus);
int luku = 12;
int tulostus = 0;

if (luku > 10) {
    System.out.println("Jee!");
    tulostus = 52;
}

System.out.println(tulostus); // toimii!
Jee!
52

Toisen säännön yhteydessä myös ensimmäinen sääntö pätee. Muuttuja on olemassa vain sen esittelyhetkestä eteenpäin, ei koskaan ennen esittelyhetkeä.

Kaverisi on hahmotellut summaamiseen ja ehtolauseen toimintaan liittyvää testiohjelmaa. Testiohjelmassa ohjelman alussa luettuun lukuun lisätään käyttäjältä luettu toinen luku. Luvun lisäämisen tulee tapahtua vain jos toisena luettu luku on positiivinen.

Ohjelma ei tällä hetkellä kuitenkaan toimi ihan toivotusti ja siinä näkyy punaista. Kaverisi ohjelma näyttää seuraavalta:

Scanner lukija = new Scanner(System.in);
int summa = Integer.parseInt(lukija.nextLine());

if (luettu > 0) {
    summa += summa + luettu;
}

int luettu = Integer.parseInt(lukija.nextLine());

System.out.println("Summa: " + summa);

Korjaa kaverisi ohjelma. Alla esimerkkitulosteita toimivasta ohjelmasta.

32
7
Summa: 39
9
-3
Summa: 9

Toistolause

Tarkastellaan seuraavaksi toistolausetta. Toistolausetta käytetään osana toistolausetta määriteltävän lohkon sisältämän lähdekoodin toistamiseen useampaan kertaan.

Ikuinen toisto

Yksinkertaisin toiston muoto on ikuinen toisto. Seuraava ohjelma jatkaa merkkijonon osaan ohjelmoida! tulostamista ikuisesti eli "äärettömän monta kertaa":

while (true) {
    System.out.println("osaan ohjelmoida!");
}

Toistolause while (true) { /* toistettava */ } saa aikaan sen, että toistolauseeseen liittyvää lohkoa, eli {}:lla ympäröityjä komentoja -- yllä System.out.println("osaan ohjelmoida!"); -- suoritetaan äärettömän monta kertaa. Lohkossa olevien lähdekoodirivien määrää ei ole rajattu.

Toistolauseesta poistuminen

Ikuinen toisto ei yleensä ole se mitä halutaan. Saat NetBeansista käynnissä olevan ohjelman sammutettua painamalla tulostusikkunan vasemmalla puolella olevaa punaista nappia. Ohjelmakoodissa toistolauseesta poistutaan komennolla break. Komento break on tyypillisesti toistolauseen lohkon sisällä olevassa ehtolauseessa, jossa tarkastellaan haluaako käyttäjä poistua toistolauseesta.

Scanner lukija = new Scanner(System.in);

while (true) {
    System.out.println("osaan ohjelmoida!");

    System.out.print("jatketaanko (ei lopettaa)? ");
    String komento = lukija.nextLine();
    if (komento.equals("ei")) {
        break;
    }
}

System.out.println("kiitos ja kuulemiin.");

Kun tietokone suorittaa komennon break, siirtyy se toistolausetta seuraavan komennon suorittamiseen.

Ylläolevassa esimerkissä toisto etenee siten että ensin tulostuu osaan ohjelmoida! ja tämän jälkeen ohjelma kysyy käyttäjältä jatketaanko vielä. Jos käyttäjä vastaa ei, suoritetaan komento break. Tällöin suoritus siirtyy toistolausetta seuraavaan komentoon, joka tulostaa merkkijonon kiitos ja kuulemiin.

osaan ohjelmoida!
jatketaanko (ei lopettaa)? joo
osaan ohjelmoida!
jatketaanko (ei lopettaa)? ja
osaan ohjelmoida!
jatketaanko (ei lopettaa)? ei
kiitos ja kuulemiin.

Tehtävärunkoon on määritelty muuttuja String salasana, jolle on asetettu arvoksi porkkana -- älä muuta tätä salasanaa. Toteuta ohjelma, jossa ohjelma kysyy käyttäjältä salasanaa ja vertailee sitä muuttujassa salasana olevaan arvoon. Salasanaa tulee kysyä uudestaan kunnes käyttäjä kirjoittaa oikean salasanan. Muistathan, että merkkijonoja vertaillaan equals-komennolla!

Anna salasana: nauris
Väärin!
Anna salasana: lanttu
Väärin!
Anna salasana: porkkana
Oikein!

Salaisuus!

Lisää ohjelmaan myös oma salainen viestisi, joka näytetään kun käyttäjä kirjoittaa salasanan oikein. Se voi olla mitä tahansa!

Anna salasana: satsuma
Väärin!
Anna salasana: nauris
Väärin!
Anna salasana: lanttu
Väärin!
Anna salasana: porkkana
Oikein!

Salaisuus on: znvavbfgv grugl!

Ylläoleva salaisuus on luotu Rot13-algoritmia käyttäen.

Tee ohjelma, joka lukee käyttäjältä lukuja ja tulostaa niiden summan. Ohjelma lopettaa kyselemisen kun syötetään luku 0. Käytä seuraavaa pohjaa, jonka saat myös palautusautomaatilta:

Scanner lukija = new Scanner(System.in);

int summa = 0;
System.out.print("Anna lukuja, nolla lopettaa: ");
while (true) {
    int luettu = Integer.parseInt(lukija.nextLine());
    if (luettu == 0) {
        break;
    }

    // TEE JOTAIN TÄÄLLÄ

}

System.out.println("Summa lopussa: " + summa);

Ohjelman tulee toimia seuraavasti:

Anna lukuja, nolla lopettaa:
3
2
1
1
0
Summa: 7

Yhteenveto: while-true-break

Tarkastellaan ohjelman suoritusta toistolauseesta alkaen. Jos toistolauseen ehto on totta -- tässä true eli aina totta -- toistettavan ohjelmakoodin suoritus alkaa. Toistettava ohjelmakoodi sijaitsee while-ehdon jälkeisen avaavan aaltosulun ja sitä vastaavan sulkevan aaltosulun rajaamalla alueella.

Suoritus etenee aivan kuten normaalin ohjelmakoodin suoritus (2), mutta, kun ohjelman suoritus päätyy toistolauseen päättävään aaltosulkuun (3), palataan kohtaan (1).

Jos suorituksessa kohdataan avainsana break, toistettavasta ohjelmakoodista poistutaan heti (4) -- tällöin ohjelman suoritus jatkuu toistettavan ohjelmakoodin alueen sulkevan aaltosulun jälkeisestä komennosta.

Esimerkki: Laskin

Seuraavassa esimerkissä on toteutettu summa- ja erotustoiminnallisuudet tarjoava laskin. Laskin kysyy käyttäjältä komentoa. Komennolla lopetus suoritetaan break ja toisto loppuu. Jos komento ei ole lopetus, kysytään kahta lukua. Jos komento oli summa lasketaan lukujen summa ja tulostetaan se. Jos komento oli erotus toimitaan vastaavasti. Muussa tapauksessa ilmoitetaan että komento on tuntematon. Lopulta palataan toistolausekkeen alkuun, ja toistolausekkeessa olevan koodin suoritus alkaa uudelleen.

System.out.println("tervetuloa käyttämään laskinta");

while (true) {
    System.out.print("anna komento (summa, erotus, lopetus): ");
    String komento = lukija.nextLine();
    if (komento.equals("lopetus")) {
        break;
    }

    System.out.print("anna luvut ");
    int eka = Integer.parseInt(lukija.nextLine());
    int toka = Integer.parseInt(lukija.nextLine());

    if (komento.equals("summa")) {
        int summa = eka + toka;
        System.out.println("lukujen summa " + summa);
    } else if (komento.equals("erotus")) {
        int erotus = eka - toka;
        System.out.println("lukujen erotus " + erotus);
    } else {
        System.out.println("tuntematon komento");
    }
}

System.out.println("kiitos ja kuulemiin.");

Screencast joka näyttää miten ohjelma syntyy:

 

Toistolauseen ehto toiston lopettajana

Komento break ei ole ainoa tapa poistua toistolauseesta eli siirtyä toistolausetta seuraavan lauseen suorittamiseen. Toistolauseen yleinen muoto on while (ehto), missä ehtona voi olla mikä tahansa totuusarvoinen lauseke. Lauseke määritellään täsmälleen samalla tavalla kuin ehtolauseen (if) ehto.

Seuraavassa esimerkissä tulostetaan luvut 1, 2, ..., 5. Kun luku-muuttujan arvo on yli 5, while-ehto ei ole enää voimassa ja toistaminen lopetetaan.

int luku = 1;

while (luku < 6) {
    System.out.println(luku);
    luku++;
}

Lue ylläoleva "niin pitkään kuin muuttujan luku arvo on pienempi kuin 6, tulosta muuttujan luku arvo ja kasvata muuttujan luku arvoa yhdellä".

Yllä muuttujan luku arvoa kasvatetaan yhdellä aina kun toistolauseen lohko suoritetaan.

int luku = 1024;

while (luku >= 1) {
    System.out.println(luku);
    luku /= 2;
}

Screencast aiheesta:

Tee ohjelma, joka tulostaa kokonaisluvut väliltä 1–100.

Ohjelman tulostus on seuraava:

1
2
3
(välissä paljon rivejä)
98
99
100

Tee ohjelma, joka tulostaa kokonaisluvut väliltä 100–1.

Ohjelman tulostus on seuraava:

100
99
98
(välissä paljon rivejä)
3
2
1

Vihje: aseta toistolauseessa käytettävän apumuuttujan arvoksi 100 ja vähennä muuttujan arvoa yhdellä toistolauseen lohkossa.

Tee ohjelma, joka tulostaa parilliset kokonaisluvut väliltä 2–100.

2
4
6
(välissä paljon rivejä)
96
98
100

Tämä tehtävä on ensimmäinen kaksiosainen tehtävä. Kun teet molemmat osat, saat tehtävästä kaksi tehtäväpistettä. Voit palauttaa tehtävän myös siten, että vain ensimmäinen osa on tehtynä.

Mihin asti?

Kirjoita ohjelma, joka tulostaa kokonaisluvut 1:stä käyttäjän antamaan lukuun asti.

Mihin asti? 3
1
2
3
Mihin asti? 5
1
2
3
4
5

Vihje: käyttäjältä lukemasi luku toimii nyt whilen lopetusehdon ylärajana. Muista että Javassa a <= b tarkoittaa a pienempi tai yhtä suuri kuin b.

Mistä lähtien?

Lisää ohjelmaan käyttäjältä kysyttävä alaraja.

Mihin asti? 8
Mistä lähtien? 5
5
6
7
8

Jos tavoite on suurempi kuin lähtökohta ei tulostu mitään:

Mihin asti? 12
Mistä lähtien? 16

Huom! muista että ala- ja yläraja voivat olla myös negatiivisia!

Toistolauseen suorituksen lopettamisesta

Toistolauseen suoritus ei lopu heti kun toistolauseen ehtolauseke voisi evaluoitua todeksi. Toistolauseen ehtolauseke evaluoidaan aina kun saavutaan toistolauseen alkuun, eli (1) kun ohjelman seuraava suoritettava lause on toistolause, ja (2) kun toistolauseeseen liittyvän lohkon sisältämän ohjelmakoodin suoritus on saatu loppuun.

Tarkastellaan seuraavaa toistolausetta.

int luku = 1;

while (luku != 2) {
    System.out.println(luku);
    luku = 2;
    System.out.println(luku);
    luku = 1;
}

Ohjelman tulostus seuraavanlainen:

1
2
1
2
1
2
...

Vaikka muuttujan luku arvo on välillä 2, toistolauseen suoritus ei lopu koskaan.

Toistolauseen ehto tarkistetaan siis vain kun toistolauseen toistaminen aloitetaan sekä silloin kun koodin suoritus on päässyt toistolauseen lopettavaan aaltosulkuun asti. Jos ehto on totta, suoritus hyppää takaisin toistolauseen alkuun, ja jos ehto on epätotta, suoritus siirtyy toistolausetta seuraavaan lauseeseen.

Vaikka muuttujan luku arvo on ylläolevassa toistolauseessa välillä 2, ei se ole koskaan 2 toistolauseen lopussa. Lopussa ehto luku != 2 on aina totta, ja suoritus jatkuu..

Toisto ja useampi muuttuja

Ohjelmat ovat usein tyyppiä "tee jotain tietty määrä kertoja". Näissä ohjelmissa esiintyy toisto, jonka jokaisella toistokerralla tehdään haluttu toiminnallisuus sekä muutetaan kertojen lukumäärää laskevaa laskurimuuttujaa.

Seuraava ohjelma laskee tulon 4*3 hieman kömpelöllä tavalla eli summana 3 + 3 + 3 + 3:

int tulos = 0;

int i = 0;
while (i < 4) {
    tulos += 3; // tarkoittaa samaa kuin tulos = tulos + 3;
    i++;  // tarkoittaa samaa kuin i = i + 1;
}

System.out.println(tulos);

Mitä enemmän ohjelmassa on muuttujia, sitä haastavampaa ohjelman askeleittaisen suorituksen seuraaminen on. Ohjelman ymmärtämisen kannalta suorituksen seuraaminen on kuitenkin tärkeää.

Yksi näppärä tapa muuttujien arvojen tarkasteluun toistolauseessa on taulukko. Seuraavaan taulukkoon on kirjoitettu auki edellisen esimerkin muuttujien tulos ja i arvot kullakin toistolauseen ehdon i < 4 vertailuhetkellä.

tulos i i < 4
0 0 true
3 1 true
6 2 true
9 3 true
12 4 false

Toistolauseen suoritus loppuu kun muuttujan summa arvo on 12 ja muuttujan i arvo on 4 (ehto i < 4 on tällöin epätotta).

Tee ohjelma, joka laskee summan 1+2+3+...+n, missä n on käyttäjän syöttämä luku.

Esimerkkitulostuksia:

Mihin asti? 3
Summa on 6

Edellisessä esimerkissä laskettiin 1 + 2 + 3 = 6

Mihin asti? 7
Summa on 28

Ja nyt laskettiin 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28

Vihje: Tee ohjelma while-komennon avulla. Käytä ohjelmassasi apumuuttujaa toistokertojen muistamiseen. Lisää jokaisella toistokerralla toistokerrat muistavan muuttujan arvo apumuuttujaan johon lasket summan arvon.

Muuta edellistä tehtävää siten, että käyttäjä määrää summan laskemisen aloituskohdan. Voit olettaa, että käyttäjä antaa ensin pienemmän luvun ja sitten suuremman luvun.

Esimerkkitulostuksia:

Ensimmäinen: 3
Viimeinen: 5
Summa on 12

Edellisessä laskettiin 3 + 4 + 5 = 12

Ensimmäinen: 2
Viimeinen: 8
Summa on 35

Ja nyt laskettiin 2 + 3 + 4 + 5 + 6 + 7 + 8 = 35

Tee ohjelma, joka laskee käyttäjän syöttämän luvun kertoman.

Kertoma n! lasketaan kaavalla 1*2*3*...*n. Esimerkiksi luvun 4 kertoma on 24, eli 4! = 1*2*3*4 = 24. Lisäksi on määritelty, että luvun 0 kertoma on 1, eli 0! = 1.

Esimerkkitulostuksia:

Anna luku: 3
Kertoma on 6

Nyt laskettiin 1 * 2 * 3 = 6

Anna luku: 10
Kertoma on 3628800

Ja nyt laskettiin 1 * 2 * 3 * ... * 8 * 9 * 10 = 3628800

Lisätietoa: Kertomaa käytetään erityisesti todennäköisyyslaskennassa tilanteissa, missä halutaan esimerkiksi tarkastella jonkin joukon kaikkia erilaisia järjestyksiä. Esimerkiksi viiden hengen ryhmän voi järjestää 5! erilaiseen jonoon, ja 52 kortin korttipakka voi olla 52! erilaisessa järjestyksessä. Kertomaa voi käyttää myös kombinaatioiden laskemiseen; esimerkiksi 52 kortin korttipakasta on mahdollista jakaa 52! / (5! * (52 - 5)!) erilaisella viiden kortin kättä, ja 40 numeron joukosta voi tehdä yhteensä 40! / (7! * (40 - 7)!) erilaista 7 numeron lottoriviä.

Kaverisi on järjestämässä pienimuotoiset kekkerit. Hän on hyvin tarkka plaseerauksen suhteen ja miettii miten vieraat pitäisi asettaa pöytään.

Auta toteuttamasi ohjelman avulla kaveriasi ymmärtämään, ettei kaikkia mahdollisia plaseerausvaihtoehtoja kannata käydä läpi pohtia.

Ohjelmaan on toteutettuna valmiiksi graafinen käyttöliittymä, mihin kaverisi voi syöttää vieraiden lukumäärän. Muuta ohjelman toimintaa siten, että ohjelma laskee plaseerausvaihtoehtojen lukumäärän, ja näyttää sen käyttäjälle. Hyödynnä tässä edellisessä tehtävässä tekemääsi kertomaa.

 

 

Huomaat todennäköisesti, että ohjelma toimii vain melko pienillä vieraslukumäärillä. Tämä liittyy siihen, minkäkokoisua lukuja muuttujamme voivat korkeintaan sisältää. Palaamme tähän myöhemmin kurssilla.

Ohjelmien tekeminen pienissä paloissa

Kun teet ohjelmaa, oli se sitten harjoitustehtävä tai oma projektisi, mieti minkälaisia osia ohjelma tarvitsee toimiakseen, ja etene näitä pieniä osia yksitellen toteuttaen. Jokaisen osan toteuttamisen jälkeen kokeile tähänastisen ohjelmasi toimintaa.

Älä koskaan yritä ratkaista koko ongelmaa kerralla, sillä tällöin ohjelman suorittaminen ja testaaminen kesken ongelmanratkaisuprosessin on vaikeaa. Aloita jollain helpolla asialla jonka tiedät varmasti osaavasi. Kun yksi ohjelman osa on saatu toimimaan, voit siirtyä ratkaisemaan seuraavaa ongelmaa.

Osa kurssin tehtävistä on valmiiksi osiin pilkottuja. Usein osat pitää vielä pilkkoa ohjelmoinnin kannalta vieläkin pienempiin paloihin. Kannattaa tehdä siten, että suoritat ohjelman lähes jokaisen uuden koodirivin jälkeen. Tällöin varmistat, että ratkaisu on etenemässä haluttuun suuntaan.

Seuraavassa tehtävässä tehdään yksi ohjelma, mutta ohjelman rakentaminen tapahtuu hyvin pienissä paloissa. Tämä on ehdottoman suositeltava tapa aina kun ohjelmoit.

Tehtäväsarja muodostaa yhden isomman ohjelman, jonka toiminnallisuus toteutetaan pienissä paloissa. Jos et tee tehtäväsarjaa loppuun asti, voit lähettää sen tarkastettavaksi vajaatekoisenakin. Tämä onnistuu painamalla testausnapin oikealla puolella olevasta "submit"-napista eli pienestä ylöspäinosoittavasta nuolesta. Vaikka palautusautomaatti valittaakin vielä tekemättä olevien tehtävänosien testeistä, kirjautuvat jo tekemiesi osien pisteet.

Huom: nyt (ja jatkossa) jokainen isomman tehtävän "alitehtävä" on saman arvoinen tehtävä kuin alikohdaton tehtävä. Tämä tehtävä vastaa siis viittä normaalia tehtävää.

Lukujen lukeminen

Tee ohjelma, joka kysyy käyttäjältä lukuja (ohjelma tulostaa käyttäjälle aluksi "Syötä luvut:"), kunnes käyttäjä antaa luvun -1. Kun käyttäjä syöttää luvun -1, ohjelma tulostaa "Kiitos ja näkemiin!" ja päättyy.

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!

Lukujen summa

Laajenna edellistä ohjelmaa siten, että ohjelma ilmoittaa käyttäjän syöttämien lukujen summan. (Lukua -1 ei lasketa mukaan.)

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!
Summa: 11

Lukujen summa ja lukumäärä

Laajenna edellistä ohjelmaa siten, että ohjelma ilmoittaa myös käyttäjien antamien lukujen lukumäärän. (Lukua -1 ei lasketa mukaan.)

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!
Summa: 11
Lukuja: 3

Lukujen keskiarvo

Muuta edellistä ohjelmaa siten, ohjelma ilmoittaa lukujen keskiarvon. (Lukua -1 ei lasketa mukaan.)

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!
Summa: 11
Lukuja: 3
Keskiarvo: 3.666666666666

Parilliset ja parittomat

Laajenna edellistä ohjelmaa siten, että ohjelma ilmoittaa parillisten ja parittomien lukujen määrän. (Lukua -1 ei lasketa mukaan.)

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!
Summa: 11
Lukuja: 3
Keskiarvo: 3.666666666666
Parillisia: 2
Parittomia: 1

Toiston jatkaminen

Törmäämme usein tilanteeseen, missä osa ohjelmalle annetuista syötteistä on epäkelpoja. Jos syötteitä käsitellään toistolauseessa, haluamme mahdollisimman nopeasti palata uuden syötteen lukemiseen. Komennolla continue siirrytään toistolauseen alkuun.

Scanner lukija = new Scanner(System.in);

System.out.print("Anna lukuja, negatiiviset luvut eivät kelpaa: ");
while (true) {
    int luettu = Integer.parseInt(lukija.nextLine());
    if (luettu < 0) {
        continue;
    }

    // tehdään jotain täällä
}

Ylläoleva ohjelma palaa heti uuden syötteen lukemiseen jos käyttäjä syöttää negatiivisen luvun.

Komentoja break ja continue käytetään usein yhdessä. Komennolla break lopetetaan toistolauseen suoritus, kun taas komennolla continue valitaan vain osa syötteistä käsiteltäväksi.

Allaoleva ohjelma laskee positiivisten syötteiden summan. Jos käyttäjä syöttää negatiivisen luvun, ohjelma lukee käyttäjältä seuraavan syötteen. Toistolauseesta poistutaan jos käyttäjä syöttää nollan.

Scanner lukija = new Scanner(System.in);

System.out.print("Anna lukuja, negatiiviset luvut eivät kelpaa: ");
int summa = 0;

while (true) {
    int luettu = Integer.parseInt(lukija.nextLine());

    if (luettu == 0) {
        break;
    }

    if (luettu < 0) {
        continue;
    }

    summa += luettu;
}

System.out.println("Hyväksyttävien lukujen summa: " + summa);

Ohjelmaan voi tuoda mukaan myös muita muuttujia. Allaolevassa esimerkissä lasketaan summan lisäksi hyväksyttyjen ja epäkelpojen lukujen lukumäärä.

Scanner lukija = new Scanner(System.in);

System.out.print("Anna lukuja, negatiiviset luvut eivät kelpaa: ");
int summa = 0;
int hyvaksytytLuvut = 0;
int epakelvotLuvut = 0;

while (true) {
    int luettu = Integer.parseInt(lukija.nextLine());

    if (luettu == 0) {
        break;
    }

    if (luettu < 0) {
        epakelvotLuvut++;
        continue;
    }

    summa += luettu;
    hyvaksytytLuvut++;
}

System.out.println("Hyväksyttävien lukujen summa: " + summa);
System.out.println("Hyväksyttyjä lukuja: " + hyvaksytytLuvut);
System.out.println("Epäkelvot luvut: " + epakelvotLuvut);

Toteuta ohjelma, joka lukee käyttäjän antamia syötteitä ja laskee kelvollisten lukujen lukumäärän. Luku on kelvollinen, jos se on suurempi tai yhtäsuuri kuin -140 ja pienempi tai yhtäsuuri kuin 20. Lopeta syötteiden lukeminen kun käyttäjä syöttää luvun 9999.

Jos käyttäjä syöttää luvun, joka on pienempi kuin -140 tai suurempi kuin 20, hänelle kerrotaan ettei syötetty luku ollut kelvollinen.

Syötä luku: 5
Syötä luku: 22
Kelvoton luku
Syötä luku: -11
Syötä luku: -140
Syötä luku: -18
Syötä luku: 9999

Kelvollisia lukuja yhteensä: 4

Toteuta ohjelma, joka lukee käyttäjän antamia syötteitä ja laskee kelvollisten lukujen summan. Luku on kelvollinen, jos se on suurempi tai yhtäsuuri kuin -140 ja pienempi tai yhtäsuuri kuin 20. Lopeta syötteiden lukeminen kun käyttäjä syöttää luvun 9999.

Jos käyttäjä syöttää luvun, joka on pienempi kuin -140 tai suurempi kuin 20, hänelle kerrotaan ettei syötetty luku ollut kelvollinen.

Syötä luku: 5
Syötä luku: 22
Kelvoton luku
Syötä luku: -11
Syötä luku: -140
Syötä luku: -18
Syötä luku: 9999

Kelvollisten lukujen summa: −164

Toteuta ohjelma Mars-planeetan lämpötilamittausten tarkasteluun. Marsin alin lämpötila (pinnalla) on -140 astetta, korkein lämpötila on 20 astetta. Ohjelmalle syötetään mittauksia kunnes käyttäjä syöttää luvun 9999. Tämän jälkeen kerrotaan lämpötilamittausten keskiarvo.

Jos käyttäjä syöttää luvun, joka on pienempi kuin -140 tai suurempi kuin 20, lukua ei huomioida lämpötilan keskiarvon laskemisessa.

Syötä mittaus: -41
Syötä mittaus: -11
Syötä mittaus: 23
Syötä mittaus: 2
Syötä mittaus: -14
Syötä mittaus: -22
Syötä mittaus: -45
Syötä mittaus: 9999

Mittausten keskiarvo: -21.833333333333332

Toteuta ohjelma, missä käyttäjän tulee arvata välillä 1-10 oleva luku. Aseta pelin arvattavaksi luvuksi 7.

Ohjelman suoritus loppuu kun käyttäjä arvaa oikean luvun (eli 7). Jos käyttäjä syöttää luvun, joka on suurempi kuin 10 tai pienempi kuin 1, käyttäjälle kerrotaan ettei hänen syöttämänsä luku ollut kelvollinen.

Ohjelman tulee kertoa käyttäjälle käyttäjän tekemien arvausten määrä. Välille 1-10 osuvat arvaukset lasketaan, mukaan lukien oikea arvaus.

Minäpä tiedän luvun väliltä 1-10, jota sinä et tiedä!

Arvaa luku: 5
Ei ollut!
Arvaa luku: 28
Epäkelpo luku!
Arvaa luku: 10
Ei ollut!
Arvaa luku: 7

Oikein! Arvauksia yhteensä: 3

Toteuta ohjelma, missä käyttäjän tulee arvata välillä 1-100 oleva luku. Aseta pelin arvattavaksi luvuksi 42.

Ohjelman suoritus loppuu kun käyttäjä arvaa oikean luvun (eli 42). Jos käyttäjä syöttää luvun, joka on suurempi kuin 100 tai pienempi kuin 1, käyttäjälle kerrotaan ettei hänen syöttämänsä luku ollut kelvollinen.

Jos käyttäjä syöttää luvun, joka on pienempi kuin arvattava luku, käyttäjälle kerrotaan että arvattava luku on suurempi. Jos taas käyttäjä syöttää luvun, joka on suurempi kuin arvattava luku, käyttäjälle kerrotaan että arvattava luku on pienempi.

Ohjelman tulee kertoa käyttäjälle myös tehtyjen arvausten määrä.

Minäpä tiedän luvun väliltä 1-100, jota sinä et tiedä!

Arvaa luku: 5
Lukuni on isompi!
Arvaa luku: 25
Lukuni on isompi!
Arvaa luku: 450
Epäkelpo luku!
Arvaa luku: 50
Lukuni on pienempi!
Arvaa luku: 45
Lukuni on pienempi!
Arvaa luku: 40
Lukuni on isompi!
Arvaa luku: 42

Oikein! Arvauksia yhteensä: 6

Kun saat ohjelman valmiiksi, palauta se. Voit halutessasi tämän jälkeen kokeilla pelin pelaamista myös satunnaisilla luvuilla. Välillä 1-100 olevan satunnaisen luvun arpominen onnistuu seuraavasti:

int luku = (int) (1 + Math.random() * 100);

Toistolauseet ja ohjelman tyyli

Tarkastellaan vielä aiemmin näkemämme ohjelman varianttia. Allaolevassa esimerkissä ohjelmoija on toteuttanut lopettamisen jälkeen tapahtuvan toiminnallisuuden osaksi ehtolausetta, missä toistolauseesta poistutaan.

Scanner lukija = new Scanner(System.in);

System.out.print("Anna lukuja, negatiiviset luvut eivät kelpaa: ");
int summa = 0;
int hyvaksytytLuvut = 0;
int epakelvotLuvut = 0;

while (true) {
    int luettu = Integer.parseInt(lukija.nextLine());

    if (luettu == 0) {
        System.out.println("Hyväksyttävien lukujen summa: " + summa);
        System.out.println("Hyväksyttyjä lukuja: " + hyvaksytytLuvut);
        System.out.println("Epäkelvot luvut: " + epakelvotLuvut);
        break;
    }

    if (luettu < 0) {
        epakelvotLuvut++;
        continue;
    }

    summa += luettu;
    hyvaksytytLuvut++;
}

Ylläoleva lähestymistapa ei ole suositeltava, sillä se johtaa helposti hyvin monimutkaiseen ohjelman rakenteeseen. Jos toistolauseen lopettamisen yhteydessä pitäisi tehdä muutakin -- esimerkiksi lukea lisää syötteitä -- asetettaisiin kyseinenkin toiminnallisuus helposti ehtolauseen sisälle, mikä johtaa yhä vaikeammin ymmärrettävään lähdekoodiin.

Pitäydytään seuraavassa toistolauseen muodossa:

Scanner lukija = new Scanner(System.in);

// toistolauseessa tarvittavien muuttujien luominen

while (true) {
    // syötteen lukeminen

    // toistolauseesta poistuminen -- break

    // epäkelpojen syötteiden rajaaminen pois -- continue

    // hyväksyttävien syötteiden käsittely
}

// toistolauseesta poistumisen jälkeen suoritettava toiminnallisuus
Ikuinen debaatti: break

Opittavan ja käytettävän toistolauseen ominaisuuksia on tarkasteltu ohjelmoinnin opetukseen liittyvässä tutkimuksessa pitkään. Eräs debaatti liittyy break-komennon käyttöön. Jotkut ovat sitä mieltä, että sen käyttäminen on yleisesti ottaen hyvä käytäntö, toiset taas ovat sitä mieltä, että se toimii esimerkiksi toistolauseen käyttöön tutustumisessa. Jotkut taas eivät suosi sen käyttämistä lainkaan.

Tutustu tätä debaattia avaavaan populääriartikkeliin Break Statement Considered. Artikkelin PDF-version pitäisi olla ladattavissa edellämainitun sivun takana olevasta linkistä.

Listarakenne

Ohjelmoidessa tulee vastaan tilanteita, joissa haluamme pitää muistissa useita arvoja. Epäkäytännöllinen tapa olisi määritellä jokaiselle arvolle oma muuttuja.

String sana1;
String sana2;
String sana3;
// ...
String sana10;

Tämä ratkaisu on oikeastaan kelvoton -- ajattele ylläoleva esimerkki vaikkapa tuhannella sanalla.

Ohjelmointikielet tarjoavat apuvälineitä, joiden avulla on helppo säilyttää useita arvoja. Tutustumme nyt Java-ohjelmointikielen ehkäpä eniten käytettyyn apuvälineeseen ArrayListiin. ArrayList on käytännössä taulukko, jonka koko kasvaa sitä mukaa kun sinne lisätään tietoa.

ArrayList-muuttujan tyyppi on ArrayList, jonka lisäksi sille määritellään listalle lisättävien arvojen tyyppi. Esimerkiksi merkkijonoja sisältävän ArrayListin tyyppi on ArrayList<String> ja kokonaislukuja sisältävän ArrayListin tyyppi on ArrayList<Integer>. Listan luominen tapahtuu komennolla new ArrayList<>();.

Yhteen listaan lisätään aina samantyyppisiä arvoja.

Seuraavassa esimerkissä esitellään merkkijonoja tallentava ArrayList, johon lisätään muutama merkkijono. Tämän jälkeen tulostetaan listan nollannessa kohdassa oleva arvo eli ensimmäinen merkkijono (ohjelmoijat aloittavat laskemisen aina nollasta, lue tarkemmin täältä -- kiitos herpsu!).

import java.util.ArrayList;

public class SanalistaEsimerkki {

    public static void main(String[] args) {
        ArrayList<String> sanalista = new ArrayList<>();

        sanalista.add("Ensimmäinen");
        sanalista.add("Toinen");

        System.out.println(sanalista.get(0));
    }
}
Ensimmäinen

Ohjelmaan on toteutettu valmiina pohja, joka lukee käyttäjältä syötteitä listalle. Syötteiden lukeminen päätetään kun käyttäjä syöttää tyhjän merkkijonon.

Ohjelma tulostaa tämän jälkeen listan ensimmäisen arvon. Muokkaa ohjelmaa siten, että ensimmäisen arvon sijaan tulostetaan kolmas arvo. Huomaa, että ohjelmoijat aloittavat laskemisen nollasta! Ohjelma saa rikkoutua rikkoutua jos listalla ei ole vähintään kolmea arvoa. Tarkastele minkälainen virheilmoitus ohjelmasta tällöin tulee.

Terho
Elina
Aleksi
Mari

Aleksi
Elina
Aleksi
Mari

Mari

Lista on erittäin hyödyllinen kun halutaan tallentaa muuttujien arvoja myöhempää käsittelyä varten. Sillä on myös helpohko tehdä virheitä.

Tehtäväpohjassa on listaa käyttävä ohjelma. Muokkaa ohjelmaa siten, että sen suorittaminen tuottaa aina virheen IndexOutOfBoundsException. Ohjelman tulee olla sellainen, että käyttäjän ei tarvitse antaa konelle syötettä (esim. näppäimistöltä).

Ohjelmassa näkyy myös toisenlainen tapa listan läpikäyntiin -- palaamme siihen myöhemmin kurssilla.

Apuvälineen tuominen ohjelman käyttöön

Jokaisella Javan tarjoamalla apuvälineellä on oma nimi ja oma sijainti. Apuvälineen tuominen ohjelman käyttöön tapahtuu import-käskyllä. Käskylle kerrotaan sijainti ja haettavan apuvälineen nimi. Esimerkiksi ArrayListin käyttöönotto vaatii komennon import java.util.ArrayList; ohjelman ylälaitaan.

import java.util.ArrayList;

public class ListaOhjelma {

    public static void main(String[] args) {
        ArrayList<String> sanalista = new ArrayList<>();

        sanalista.add("Ensimmäinen");
        sanalista.add("Toinen");
    }
}

Sama pätee myös muillekin Javan tarjoamille apuvälineille. Olemmekin jo aiemmin käyttäneet lukemiseen tarkoitettua Scanner-apuvälinettä, jonka on saanut käyttöön komennolla import java.util.Scanner;

Useamman apuvälineen käyttöönotto on helppoa. Käyttöön tuotavat apuvälineet listataan allekkain ohjelman ylälaitaan.

import java.util.ArrayList;
import java.util.Scanner;

public class ListaOhjelma {

    public static void main(String[] args) {
        Scanner lukija = new Scanner(System.in);
        ArrayList<String> sanalista = new ArrayList<>();

        sanalista.add("Ensimmäinen");
        sanalista.add(lukija.nextLine());
    }
}

Listaan liittyviä komentoja eli metodeja

Listalle on tarjolla useita hyödyllisiä komentoja. Näitä komentoja kutsutaan metodeiksi -- metodissa määritelty toiminnallisuus suoritetaan aina sille listalle, mihin liittyen metodia kutsutaan -- yhteys määritellään pisteellä.

ArrayList<String> opettajat = new ArrayList<>();

opettajat.add("Veera");
opettajat.add("Jyri");
opettajat.add("Verna");
opettajat.add("Mikko");
opettajat.add("Pihla");
opettajat.add("Sami");

System.out.println("opettajien lukumäärä " + opettajat.size());

System.out.println("listalla ensimmäisenä " + opettajat.get(0));
System.out.println("listalla kolmantena " + opettajat.get(2));

opettajat.remove("Juha");

if (opettajat.contains("Juha")) {
    System.out.println("Juha on opettajien listalla");
} else {
    System.out.println("Juha ei ole opettajien listalla");
}
opettajien lukumäärä 6
listalla ensimmäisena Veera
listalla kolmantena Verna
Juha ei ole opettajien listalla

Alla yhteenveto listan metodeista.

Listalle lisääminen: add

Listan metodi add lisää listalle metodille parametrina annetun arvon.

ArrayList<String> lista = new ArrayList<>();

// listalle lisääminen
lista.add("Arvo");

Listalla olevien arvojen lukumäärä: size

Listan metodi size palauttaa listalla olevien arvojen lukumäärän. Lukumäärä on kokonaisluku ja sitä voidaan käyttää osana lauseketta tai se voidaan asettaa kokonaislukumuuttujaan myöhempää käyttöä varten.

ArrayList<String> lista = new ArrayList<>();
System.out.println("Listalla arvoja: " + lista.size());

lista.add("Eka");
System.out.println("Listalla arvoja: " + lista.size());

int arvoja = lista.size();

lista.add("Toka");
System.out.println("Listalla arvoja: " + arvoja);
Listalla arvoja: 0
Listalla arvoja: 1
Listalla arvoja: 1

Tehtäväpohjassa on ohjelma, joka lukee käyttäjältä syötteitä. Muokkaa ohjelman toimintaa siten, että kun syötteiden lukeminen lopetetaan, ohjelma tulostaa listalla olevien arvojen lukumäärän.

Terho
Elina
Aleksi
Mari

Yhteensä: 4
Juno
Elizabeth
Mauri
Irene
Outi
Lauri
Iisa
Risto
Markus
Ville
Oskari

Yhteensä: 11

Listan tietystä kohdasta hakeminen: get

Listan metodi get palauttaa listan parametrina annettuun paikkaan liittyvä sisältö. Parametri annetaan kokonaislukuna. Listan paikkojen numerointi alkaa nollasta, eli ensimmäisenä lisätty arvo on paikassa numero 0, toisena lisätty paikassa numero 1 jne.

ArrayList<String> lista = new ArrayList<>();

lista.add("Eka");
lista.add("Toka");
lista.add("Kolmas");

System.out.println("Paikka 1 eli toinen paikka: " + lista.get(1));
System.out.println("Paikka 0 eli ensimmäinen paikka: " + lista.get(0));
Toka
Eka

Tehtäväpohjassa on ohjelma, joka lukee käyttäjältä syötteitä. Muokkaa ohjelman toimintaa siten, että kun syötteiden lukeminen lopetetaan, ohjelma tulostaa kolmanneksi viimeisenä luetun arvon. Voit olettaa, että listalle luetaan vähintään 3 arvoa.

Terho
Elina
Aleksi
Mari

Elina
Juno
Elizabeth
Mauri
Irene
Outi
Lauri
Iisa
Risto
Markus
Ville
Oskari

Markus

Huom! Listan koon palauttava metodi on tässä hyödyllinen.

Listan tietystä kohdasta poistaminen: remove

Listan metodi remove poistaa listalta parametrina annettuun paikkaan liittyvän arvon. Parametri annetaan kokonaislukuna.

ArrayList<String> lista = new ArrayList<>();

lista.add("Eka");
lista.add("Toka");
lista.add("Kolmas");

lista.remove(1);

System.out.println("Paikka 0 eli ensimmäinen: " + lista.get(0));
System.out.println("Paikka 1 eli toinen: " + lista.get(1));
Eka
Kolmas

Jos parametri on listan sisältämien arvojen tyyppinen, mutta ei kokonaisluku (kokonaislukua käytetään paikasta poistamiseen), voidaan sitä käyttää arvon poistamiseen listalta suoraan.

ArrayList<String> lista = new ArrayList<>();

lista.add("Eka");
lista.add("Toka");
lista.add("Kolmas");

lista.remove("Eka");

System.out.println("Paikka 0 eli ensimmäinen: " + lista.get(0));
System.out.println("Paikka 1 eli toinen: " + lista.get(1));
Toka
Kolmas

Onko listalla: contains

Listan metodi contains kertoo löytyykö haettua arvoa listalta. Metodi saa haettavan arvon parametrina, ja se palauttaa totuustyyppisen arvon (boolean) true tai false.

ArrayList<String> lista = new ArrayList<>();

lista.add("Eka");
lista.add("Toka");
lista.add("Kolmas");

System.out.println("Löytyykö eka? " + lista.contains("Eka"));

if (lista.contains("Toka")) {
    System.out.println("Toka löytyi");
}
Löytyykö eka? true
Toka löytyi

Tehtäväpohjassa on ohjelma, joka lukee käyttäjältä syötteitä. Lisää ohjelmaan toiminnallisuus, missä syötteiden lukemisen jälkeen kysytään vielä yhtä merkkijonoa. Ohjelma kertoo tämän jälkeen löytyikö käyttäjän syöttämä merkkijono listalta vai ei.

Terho
Elina
Aleksi
Mari

Ketä etsitään? Mari
Mari löytyi!
Terho
Elina
Aleksi
Mari

Ketä etsitään? Arto
Arto ei löytynyt!
Listalla olevan arvon paikka eli indeksi

Listan paikkojen numerointi eli indeksointi alkaa aina nollasta. Listan ensimmäinen arvo on indeksissä 0, toinen arvo indeksissä 1, kolmas arvo indeksissä 2 ja niin edelleen.

Ylläolevassa listassa ensimmäisenä on arvo 6 ja toisena arvo 1. Jos ylläolevaan listaan lisättäisiin uusi arvo kutsumalla luvut-listan metodia add parametrilla 8, menisi luku 8 listan indeksiin 6 eli seitsemänneksi luvuksi.

Vastaavasti kutsumalla metodia get parametrilla 4, listalta haettaisiin viidettä lukua.

Listan läpikäynti

Seuraavassa esimerkissä listalle lisätään neljä nimeä, jonka jälkeen listan sisältö tulostetaan.

ArrayList<String> opettajat = new ArrayList<>();

opettajat.add("Sami");
opettajat.add("Samu");
opettajat.add("Anne");
opettajat.add("Anna");

System.out.println(opettajat.get(0));
System.out.println(opettajat.get(1));
System.out.println(opettajat.get(2));
System.out.println(opettajat.get(3));
Sami
Samu
Anne
Anna

Esimerkki on kömpelö. Entä jos listalla olisi enemmän arvoja? Tai vähemmän? Entäs jos emme tietäisi listalla olevien arvojen määrää?

Tehdään ensin välivaiheen versio jossa pidetään kirjaa tulostettavasta paikasta muuttujan paikka avulla:

ArrayList<String> opettajat = new ArrayList<>();

opettajat.add("Sami");
opettajat.add("Samu");
opettajat.add("Anne");
opettajat.add("Anna");

int paikka = 0;

System.out.println(opettajat.get(paikka));
paikka++;

System.out.println(opettajat.get(paikka));  // paikka = 1
paikka++;

System.out.println(opettajat.get(paikka));  // paikka = 2
paikka++;

System.out.println(opettajat.get(paikka));  // paikka = 3
paikka++;

Huomaamme, että ylläolevassa ohjelmassa on toistoa. Voimme hyödyntää toistolausetta ja kasvattaa muuttujaa paikka niin kauan kunnes se kasvaa liian suureksi:

ArrayList<String> opettajat = new ArrayList<>();

opettajat.add("Sami");
opettajat.add("Samu");
opettajat.add("Anne");
opettajat.add("Anna");

int paikka = 0;
while (paikka < opettajat.size()) {
    System.out.println(opettajat.get(paikka));
    paikka++;
}

// muistatko miksi paikka <= opettajat.size() ei toimi?

Nyt tulostus toimii riippumatta listalla olevien alkioiden määrästä.

Listalle lisättävien muuttujien tyyppi

Lista voi sisältää vain tietyntyyppisiä arvoja. Nämä arvot määritellään listaa luodessa, esimerkiksi ArrayList<String> sisältää merkkijonotyyppisiä muuttujia.

Aiemmin käyttämämme muuttujatyypit int ja double sekä lähes kaikki muut pienellä alkukirjaimella kirjoitettavat muuttujat ovat sellaisia, että ne eivät käy suoraan listan sisältämien alkioiden tyypiksi. Jos haluamme lisätä int-tyyppisiä muuttujia listalle, tulee listan sisältämien arvojen tyypiksi asettaa Integer. Vastaavasti double-tyyppiset muuttujat lisätään listalle, jonka sisältämien arvojen tyypiksi on asetettu Double.

ArrayList<String> nimet = new ArrayList<>();

nimet.add("Sami");
nimet.add("Samu");
nimet.add("Anne");
nimet.add("Anna");


ArrayList<Integer> iat = new ArrayList<>();

iat.add(6);
iat.add(11);
iat.add(38);
iat.add(14);

int paikka = 0;
while (paikka < 4) {
    System.out.print("Nimi: " + nimet.get(paikka));
    System.out.print("\t");

    int ika = iat.get(paikka);
    System.out.println("Ikä: " + ika);

    // tai:
    // System.out.print("Ikä: " + iat.get(paikka));
    System.out.println();

    paikka++;
}
Nimi: Sami    Ikä 6
Nimi: Samu    Ikä 11
Nimi: Anne    Ikä 38
Nimi: Anna    Ikä 14
Miksei ArrayList<int> toimi?

Tämä rajoitus liittyy siihen, miten ArrayList on toteutettu. Javan muuttujat voidaan jakaa kahteen kategoriaan: alkeistyyppisiin muuttujiin ja viittaustyyppisiin muuttujiin. Alkeistyyppiset muuttujat kuten int ja double sisältävät niihin liittyvät arvot suoraan. Viittaustyyppiset muuttujat taas, kuten esimerkiksi ArrayList sisältävät viitteen paikkaan, joka sisältää muuttujaan liittyvät arvot.

Hieman yksinkertaistaen: alkeistyyppiset muuttujat pystyvät sisältämään vain rajatun määrän tietoa, kun taas viitteen taakse tietoa voi säilöä lähes rajattomasti.

ArrayList olettaa, että sen sisältämät muuttujat ovat viittaustyyppisiä. Java muuntaa automaattisesti int-tyyppisen muuttujan Integer-tyyppiseksi kun se lisätään listalle, sama tapahtuu myös kun muuttuja haetaan listalta. Vastaava muunnos tapahtuu myös double-tyyppiselle muuttujalle, josta tulee Double-tyyppinen muuttuja.

Palaamme tähän jatkossakin, sillä muuttujat vaikuttavat oleellisesti ohjelmamme suoritukseen.

Ohjelmaan on toteutettu valmiina pohja, joka lukee käyttäjältä lukuja listalle. Syötteiden lukeminen päätetään kun käyttäjä syöttää luvun -1.

Lisää ohjelmaan toiminnallisuus, joka lukujen lukemisen jälkeen tulostaa listalla olevien lukujen summan.

72
2
8
11
-1

Summa: 93

Ohjelmaan on toteutettu valmiina pohja, joka lukee käyttäjältä lukuja listalle. Syötteiden lukeminen päätetään kun käyttäjä syöttää luvun -1.

Lisää ohjelmaan toiminnallisuus, joka etsii listalta listan suurimman luvun ja tulostaa sen arvon. Ohjelman pitäisi toimia seuraavasti.

72
2
8
93
11
-1

Listan suurin luku: 93

Ota mallia allaolevasta pienintä lukua etsivästä lähdekoodista.

// oletetaan, että käytössämme on lista, jossa on kokonaislukuja

int pienin = lista.get(0);

int indeksi = 0;
while (indeksi < lista.size()) {
    int luku = lista.get(indeksi);
    if (pienin > luku) {
        pienin = luku;
    }
    indeksi++;
}

System.out.println("Listan pienin luku: " + pienin);

Toteuta ohjelma, joka lukee käyttäjältä lukuja listalle. Syötteiden lukeminen päätetään kun käyttäjä syöttää luvun -1.

Kun lukujen lukeminen lopetetaan, laske listalla olevien lukujen keskiarvo ja tulosta se.

72
2
8
11
-1

Keskiarvo: 23.25

Ohjelmaan on toteutettu valmiina pohja, joka lukee käyttäjältä lukuja listalle. Syötteiden lukeminen päätetään kun käyttäjä syöttää luvun -1.

Lisää ohjelmaan toiminnallisuus, joka kysyy käyttäjältä lukua ja kertoo luvun indeksin. Jos lukua ei löydy, tulee siitä ilmoittaa erikseen (vihje: contains!).

72
2
8
8
11
-1

Mitä etsitään? 2
Luku 2 on indeksissä 1
72
2
8
8
11
-1

Mitä etsitään? 7
Lukua 7 ei löydy.
72
2
8
8
11
-1

Mitä etsitään? 8
Luku 8 on indeksissä 2
Luku 8 on indeksissä 3

Toteuta ohjelma, joka lukee käyttäjältä lukuja. Kun käyttäjä syöttää luvun 9999, lukujen lukeminen lopetetaan. Ohjelma tulostaa tämän jälkeen pienimmän listalla olevan luvun sekä indeksit, joista pienin luku löytyy. Pienin luku voi siis esiintyä useamman kerran.

72
2
8
8
11
9999

Pienin luku on 2
Pienin luku löytyy indeksistä 1
72
44
8
8
11
9999


Pienin luku on 8
Pienin luku löytyy indeksistä 2
Pienin luku löytyy indeksistä 3

Fibonaccin lukujonon ajatuksena on laskea yhteen kaksi edellistä lukua, ja näin saada seuraavan luvun arvo. Lukujonon ensimmäiset kaksi lukua ovat 0 ja 1. Seuraavan luvun saa laskettua aina kahden edellisen luvun summana.

Toteuta ohjelma, joka ensin laskee Fibonaccin lukujonon ensimmäiset 40 lukua listalle. Ohjelma kysyy tämän jälkeen käyttäjältä halutun Fibonaccin luvun kohtaa. Kun käyttäjä syöttää luvun -- oleta, että luku on indeksi listalla -- ohjelma tulostaa halutun luvun.

Ohjelman suoritus päättyy kun käyttäjä syöttää luvun -1.

Monesko luku? 0
0
Monesko luku? 1
1
Monesko luku? 7
13
Monesko luku? 22
17711
Monesko luku? -1

Kiitos!

Kivi, paperi ja sakset -- tuttavallisemmin KPS -- on kaksinpeli. Pelissä kumpikin pelaajista valitsee samaan aikaan siirron, joka voi olla "Kivi", "Paperi" tai "Sakset".

Voitto tai tappio määritellään seuraavalla tavalla.

Pelaaja 1 Pelaaja 2 Voittaja
Kivi Sakset Pelaaja 1
Kivi Paperi Pelaaja 2
Kivi Kivi Tasapeli
Sakset Paperi Pelaaja 1
...

Tehtäväpohjan mukana on tullut sovellus, jossa on mukana kaksi erillistä tiedostoa. Toisen tiedoston nimi on KiviPaperiSakset.java ja toisen tiedoston nimi on Aly.java. Tiedosto Aly.java sisältää "älyn", jota käytetään pelaajaa vastaan pelaamisessa.

Älyllä on käytössään lista, johon pelaajan vastauksia lisätään sitä mukaa kun pelaaja pelaa.

Äly on kuitenkin vielä hieman.. yksinkertainen. Se pelaa aina kiven.

Tutustu tehtäväpohjaan ja tiedostoon Aly.java. Pohdi miten älystä saisi paremman ja toteuta siihen parannus.

Tässä tehtävässä ei ole testejä, joten voit viritellä älyä niin paljon kuin haluat. Kun olet valmis, palauta tehtävä palvelimelle.

Toinen osio päättyy tähän. Jos mietit minkälaisten pulmien kanssa painit viime osion alussa ja minkälaisia pulmia saat jo nyt ratkottua, huomaat hyvin todennäköisesti kehitystä. Ohjelmoinnin oppiminen on harjoittelua ja virheitä täynnä oleva tie, jonka ensimmäiset askeleet olet jo ottanut. Huumorimielessä joku voisi sanoa, että olet tehnyt myös varmasti jo ensimmäisen virheen, eli päättänyt aloittaa ohjelmoinnin opiskelun.

Sisällysluettelo