Helsingin yliopisto / Tietojenkäsittelytieteen laitos
Copyright © 2005 Arto Wikla. Tämän oppimateriaalin käyttö on sallittu vain yksityishenkilöille opiskelutarkoituksissa. Materiaalin käyttö muihin tarkoituksiin, kuten kaupallisilla tai muilla kursseilla, on kielletty.

2.4 Loogisia lausekkeita, valintaa ja toistoa

(Muutettu viimeksi 10.9.2009, edellinen muutos 26.5.2005)

Tässä luvussa opetellaan ehtolausekkeiden kirjoittamista ja niiden käyttöä algoritmin toiminnan ohjaamiseen.

Tähän mennessä opitut ohjelmat ovat vähän tylsiä: ne suoritetaan aina samalla tavoin ensimmäisestä lauseesta viimeiseen. Valintalauseella ja ehdollisella toistolla algoritmin toiminta saadaan riippumaan syöttötiedoista. Toistolauseet ovat tavallaan varsinainen syy siihen, että tietokoneita kannattaa käyttää algoritmien suorittamiseen: vaikkei tietokoneista mihinkään 'viisaaseen' olisikaan, ne ovat hyvin nopeita mekaanisesti toistamaan alialgoritmeja. Ja ehkä viisas ohjelmoija voi saada koneenkin vaikuttamaan viisaalta...

Loogisia lausekkeita

Loogisilla lausekkeilla esitetään väitteitä ohjelman tilasta eli ohjelman muuttujien arvoista. Väitteiden totuusarvoa - totta tai ei totta - käytetään ohjaamaan algoritmin suoritusta.

Jo luvussa 2.2 tutustuttiin ohimennen loogisiin muuttujiin ja vakioihin:

  boolean oikein;
  oikein = false;
Loogisia lausekkeita (eli totuusarvoisia lausekkeita) muodostetaan tavallisimmin erilaisista vertailuista, joita mahdollisesti liitetään toisiinsa loogisilla operaatioilla. Loogisissa lausekkeissa voi toki käyttää myös totuusarvoisia muuttujia (boolean) ja vakioita (true ja false)

Vertailuoperaatioita:

   >    suurempi kuin
   >=   suurempi tai yhtäsuuri kuin
   <    pienempi kuin
   <=   pienempi tai yhtäsuuri kuin
   ==   yhtäsuuri kuin
   !=   erisuuri kuin
Kaikki vertailut tuottavat totuusarvon true tai false.

On syytä pitää mielessa sijoitusoperaation "=" ja vertailuoperaation "==" ero:

   i = 1;  // "i saa arvokseen yksi"
   i == 1  // "onko i:n arvo yksi"

Suuremmuutta tai pienemmyyttä tutkivilla operaatioilla voi vertailla vain numeerisia lausekkeita, yhtäsuuruus ja erisuuruus ovat käytettävissä monien muidenkin arvojen vertailussa.

Esimerkki: Olkoon ohjelmassa vaikkapa seuraavat määrittelyt:

   int i = 7;
   double d = 3.14;
   boolean b = false;
Tässä tilanteessa looginen lauseke
   (i < 10)     on arvoltaan true
   (d > 10.0)   on arvoltaan false
   (8 == (i+1)) on arvoltaan true
   (b != true)  on arvoltaan true
   
Huom: Liukulukujen (so. desimaalilukujen) vertailussa ei ole syytä käyttää yhtäsuuruusvertailuja (== ja !=), koska ns. pyöristysvirheiden takia yhtäsuuruusvertailut voivat antaa odottamattomia tuloksia. Tällaisessa tilanteessa on parempi tutkia lukujen erotuksen itseisarvon pienuutta. Välineitä tähän opitaan aikanaan.

Huom: String-arvojen vertailussa "==" ja "!=" eivät tarkoita sisältöjen vertailua vaan merkkijononojen sijaintipaikkojen vertailua, koska String-arvot ovat olioita. String-vertailuun käytetään erikoisvälineitä. Asia opitaan myöhemmin.

Loogisia operaatioita:

   &&    ehdollinen "ja",  "and"
   ||    ehdollinen "tai", "or"
   ^     poissulkeva tai,  "xor" (myös !=)
   !     negaatio,  "ei"   "not"
Näiden operaatioiden ns. operandit eli laskettavat voivat olla vain totuusarvoisia lausekkeita, tavallisimmin vertailuja.

Operaation ehdollisuus tarkoittaa sitä, että operaation jälkimmäinen laskettava lasketaan vain, jos totuusarvo ei selviä jo ensimmäisestä! (Ehdottomat and ja or ovat "&" ja "|". Näitä käytettäessä molemmat operandit lasketaan aina.)

Esimerkki: Olkoon ohjelmassa vaikkapa seuraavat määrittelyt:

   int i = 7;
   double d = 3.14;
   boolean b = false;
Tässä tilanteessa looginen lauseke
   (i == 7) && (d > 10.0)    on arvoltaan false
   (i == 7) && (d < 10.0)    on arvoltaan true

   (i == 7) || (d > 10.0)    on arvoltaan true
   (i != 7) || (d > 10.0)    on arvoltaan false
   !(i == 7) || (d > 10.0)   on arvoltaan false

   (b == true) || !(b == false) on arvoltaan false 

Huom: !(i==7) ja (i!=7) tarkoittavat samaa. Ensimmäinen voitaisiin lukea "ei ole niin että i on seitsemän, toinen "i ei ole seitsemän".

Loogisien operaatioiden totuustaulukko (a ja b voivat itse olla mitä tahansa loogisia lausekkeita, esimerkiksi vertailuja):

     a    |   b   ||  a && b  |  a || b  |  a ^ b   |   !a 
    --------------------------------------------------------
    true  | true  ||  true    |  true    |  false   |  false
    true  | false ||  false   |  true    |  true    |  false
    false | true  ||  false   |  true    |  true    |  true
    false | false ||  false   |  false   |  false   |  true


Valintoja ja koottu lause

Useimmiten loogisia lausekkeita käytetään ohjaamaan jotakin rakenteista lausetta, esimerkiksi valintaa.

Ehdollinen lause eli if-lause on muotoa:

  if (ehto)
    lause

tai
  if (ehto)
    lause1
  else
    lause2

Ensimmäinen tarkoittaa, että jos ehto on true, lause suoritetaan. Jälkimmäinen, että jos ehto on true, lause1 suoritetaan, muuten suoritetaan lause2.

Esimerkki (KumpiSuurempi.java): if-lauseita ilman else-osaa:

    int luku1, luku2;

    System.out.println("Anna kaksi lukua!");
    luku1 = lukija.nextInt();
    luku2 = lukija.nextInt();

    if (luku1 == luku2)
      System.out.println("Luvut ovat yhtä suuret.");

    if (luku1 > luku2)
      System.out.println("Ensimmäinen luku on suurempi.");

    if (luku1 < luku2)
      System.out.println("Toinen luku on suurempi.");
Huom: Tässä esimerkissä tietokone joutuu tekemään hieman ylimääräistä työtä; jos vaikka luvut olisivat samat, kone joka tapauksessa tutkisi myös suuremmuuden ja pienemmyyden. Asia voidaan korjata käyttämällä if-lauseissa else-osaa. Tähän palataan pian.

Esimerkki (Samat.java): if-lause else-osalla:


   if (luku1 == luku2)
      System.out.println("Luvut ovat yhtä suuret.");
   else
      System.out.println("Luvut eivät ole yhtä suuret.");

Jos valintavaihtoehto - valittava alialgoritmi - muodostuu useammasta lauseesta, nuo lauseet on koottava yhteen ns. lohkoksi (block) eli "kootuksi lauseeksi". Lohko on merkkien "{" ja "}" välissä oleva lauseiden jono. Noita merkkejä sanotaan joskus käskysulkeiksi. (Huomaa miten näitä samoja merkkejä käytetään luokan ja pääohjelman rajaamiseen!)

Esimerkki: alialgoritmeissa useita lauseita:

    if (luku1 > luku2) {
      System.out.println("Korvataan luku1 luku2:lla");
      luku1 = luku2;
    }
    else {
      System.out.println("Korvataan luku2 luku1:llä");
      luku2 = luku1;
    }
Rakenteisen lauseen alilause voi olla itsekin rakenteinen lause, jonka alilause voi olla rakenteinen lause, ... Näistä nähdään lukuisia esimerkkejä myöhemmin.

Melko usein algoritmeja laadittaessa ehdollisia lauseita joudutaan ketjuttamaan.

Esimerkki: Halutaan double-muuttujan a arvosta riippuen suorittaa eri lause seuraavissa tilanteissa:

Syntyy pitkä ketju if-lauseita, joiden else-osan alilause on if-lause:
   if ( a < 0 )
     lause1;
   else
     if ( a >=1  &&  a < 50 )
       lause2;
     else
       if ( a > 61 && a < 103 )
         lause3;
       else
         if ( a >= 203 && a <= 429 )
            lause4;
         else
           if ( a >= 929 && a < 1021 )
             lause5;
           else
             if ( a >= 1621 && a < 5000 )
               lause6;
             else
               lause7;

Tässä tilanteessa ohjelmoija kuitenkin mieltää arvovälit ennemminkin rinnakkaisiksi vaihtoehdoiksi, kuin valintojen alivalinnoiksi. Näin seuraava ulkoasu vastaa paremmin ohjelmoijan ajattelua:
   if ( a < 0 )
     lause1;
   else if ( a >=1  &&  a < 50 )
     lause2;
   else if ( a > 61 && a < 103 )
     lause3;
   else if ( a >= 203 && a <= 429 )
     lause4;
   else if ( a >= 929 && a < 1021 )
     lause5;
   else if ( a >= 1621 && a < 5000 )
     lause6;
   else
    lause7;

Huom: Ohjelman ulkoasu, 'lay-out', on syytä aina laatia sellaiseksi, että ohjelman toimintalogiikka on helppo hahmottaa. Alilauseet sisennetään, välilyöntejä ja tyhjiä rivejä on syytä käyttää erottamaan rakenteita toisistaan, ...

Mitä seuraava kääntäjän kannalta ihan kelvollinen ohjelmanpätkä tekee?

if (a<0) lause1; else if (a>=0&&a<51) lause2;else if(a 
>=51&&a<103) lause3;else if (a>=103&&a<429) lause4;else 
if (a>=429&&a<1021) lause5;else if (a>=1021&&a<5000)
lause6;else lause7;


Edellä nähty esimerkki (KumpiSuurempi.java) on järkevää ohjelmoida seuraavasti:
    if (luku1 == luku2)
      System.out.println("Luvut ovat yhtä suuret.");
    else if (luku1 > luku2)
      System.out.println("Ensimmäinen luku on suurempi.");
    else   // nyt tiedetään varmasti että luku1 < luku2 ! 
      System.out.println("Toinen luku on suurempi.");
Nyt tietokone ei joudu tekemään turhaa työtä!

Esimerkki ohjelmointikielen määrittelijän ja toteuttajan ongelmasta:
Koska if-lauseita on kahdenlaisia - niihin joko liittyy tai ei liity else-osa - syntyy seuraavanlainen moniselitteisyysongelma:
Tarkoittaako

   if (a<b) if (c<d) e = f; else g=h; 
toimintaa:
   if (a<b)                    if (a<b) 
      if (c<d)                   if (c<d)
           e = f;      VAI          e = f;
   else                          else 
      g=h;                          g=h;
Toisin sanoen mihin if-lauseeseen esimerkin else-osa liittyy? (Kääntäjälle tekstin sisennyksellä ei ole viestiä! Ohjelmoijalle se on välttämätöntä!)
Ongelma on ratkaistu Javassa ja monessa muussa ohjelmointikielessa säännöllä: else-osa liittyy lähimpään edeltävään if-lauseeseen, johon ei ole vielä liittynyt else-osaa. Esimerkkitapauksessa siis jälkimmäinen tulkinta pätee. Ensimmäinen tulkinta saadaan kun suljetaan else-osaton if-lause omaksi lohkokseen:
  if (a<b) {
    if (c<d)
       e = f;
  }
  else
    g=h;


Toistoja: for-lause

Yleensä ohjelmointikielissä on lause, jolla voi vaivattomasti käydä läpi joitakin arvoalueita. Usein algoritmeissa on esimerkiksi tarpeen tehdä jotakin peräkkäisillä arvoilla 0, 1, 2, ..., n.

Java-kielen for-toistolauseen muoto on

  for (alkuasetus; jatkamisehto; eteneminen)
    toistettava lause

Hyvin tavallinen ja luonteva käyttötapa for-lauseelle on arvoalueen läpikäynti:

     int i;
     for (i=0; i<6; ++i)
       System.out.println(i);
Lauseet tulostavat:
0
1
2
3
4
5

Huom: Tällainen toisto on tapana Javalla ohjelmoida toisin. Tapana on määritellä "askelmuuttuja" for-lauseen otsikossa:
     for (int i=0; i<6; ++i)
       System.out.println(i);
For-lauseen alkuasetuksessa voidaan siis määritellä muuttuja! Tällainen muuttuja on käytettävissä vain tuon for-lauseen sisällä.

For-lauseen toistama alialgoritmi voi toki sisältää for-toiston:

     for (int i=0; i<3; ++i)
       for (int j=0; j<2; ++j)
         System.out.println(i+"  "+j);
Tulostus:
0  0
0  1
1  0
1  1
2  0
2  1 

For-lauseessa voidaan käyttää int-tyyppisen "askelmuuttujan" sijaan myös vaikkapa double-muuttujaa. Ja eteneminen voi olla muutakin kuin ykkösellä kasvattamista tai vähentämistä:
     //Tulostetaan 3.14:n kahtakymmentä pienemmät monikerrat:
     for (double piit = 3.14; piit < 20; piit+=3.14)
       System.out.println(piit);
Tulostus:
3.14
6.28
9.42
12.56
15.700000000000001
18.84

(Yllä nähdään esimerkki ns. pyöristysvirheestä: kun lasketaan 12.56+3.14 saadaan luku, joka tietokoneen esitystavalla onkin hieman suurempi kuin 15.70!)

For-lauseen osat (alkuasetus, jatkamisehto, eteneminen, toistettava lause) voivat olla monimutkaisempiakin. Toistaiseksi tyydymme käyttämään for-lausetta arvoalueiden läpikäyntiin.

Huom: Javan versio 1.5. toi mukanaan uudenlaisen version myös for-lauseesta. Siinen tutustutaan taulukkojen yhteydessä.

Toistoja: while-lause

Ns. alkuehtoisen toiston idea on, että ennen jokaisen toistokerran aloittamista lasketaan toistoehto. Jos ehto sallii, toistetaan toistettava alialgoritmi kerran ja taas tutkitaan toistoehto, jne.

Tämä toistotapa soveltuu tilanteeseen, jossa toistokertoja tarvitaan nolla tai enemmän. While-toistossa alialgoritmia ei siis välttämättä toisteta kertaakaan.

While-toisto on muotoa

   while (jatkamisehto) 
     lause
Huom: toistettavan alialgoritmin on vaikutettava toiston ehtoon siten, että ehto joskus saa arvon false! (Miten muuten käy?)

Edellä nähty for-esimerkki:

     int i;
     for (i=0; i<6; ++i)
       System.out.println(i);
voidaan ohjelmoida while-lauseella:
     int i=0;
     while (i<6) {
       System.out.println(i);
       ++i;
     }

Laaditaan esimerkkiohjelma Tuplatw.java, joka tulostaa syöttölukuja kaksinkertaisina. Tuplattavien lukujen lukumäärää ei tiedetä etukäteen. Kun käyttäjä syöttää nollan, ohjelman suoritus päättyy.
import java.util.Scanner;

public class Tuplatw {
  private static Scanner lukija = new Scanner(System.in);

  public static void main(String[] args) {
    System.out.println("Lukujen tuplausohjelma, nolla lopettaa");
    int luku = lukija.nextInt();
    while (luku != 0) {
      System.out.println("Tuplana: "+luku*2);
      luku = lukija.nextInt();
     }
    System.out.println("Siinä ne olivat.");
  }
} 
Ohjelman käyttö voi näyttää vaikkapa seuraavalta:
Lukujen tuplausohjelma, nolla lopettaa
3
Tuplana: 6
-8
Tuplana: -16
0
Siinä ne olivat.

[Sama ohjelma voidaan toteuttaa for-lauseella (Tuplatf1.java):

     System.out.println("Lukujen tuplausohjelma, nolla lopettaa");

     for  (int luku=lukija.nextInt();  // alustus
           luku != 0;                  // jatkamisehto
           luku=lukija.nextInt()) {    // eteneminen

        System.out.println("Tuplana: "+luku*2);
     }
    System.out.println("Siinä ne olivat.");

Tämän ratkaisun tyylikkyys on makuasia. (Vielä eksoottisempi ratkaisu on Tuplatf2.java.)
]

Toistoja: do-while -lause

Ns. loppuehtoisen toiston idea on, että ensin suoritetaan toistettava alialgoritmi kerran. Sitten lasketaan toistoehto. Jos ehto sallii, toistetaan toistettava alialgoritmi uudelleen ja taas tutkitaan toistoehto, jne.

Tämä toistotapa soveltuu tilanteeseen, jossa toistokertoja tarvitaan yksi tai enemmän, toistettava alialgoritmi siis suoritetaan ainakin kerran.

Do-while -toisto on muotoa

   do
     lause
   while (jatkamisehto)
Huom: toistettavan alialgoritmin on vaikutettava toiston ehtoon siten, että ehto joskus saa arvon false! (Miten muuten käy?)

Edellä nähdyt for- ja while-esimerkit

     int i;
     for (i=0; i<6; ++i)
       System.out.println(i);
ja
     int i=0;
     while (i<6) {
       System.out.println(i);
       ++i;
     }
voidaan ohjelmoida do-while -toistolla:
     int i=0;
     do {
       System.out.println(i);
       ++i;
     } while (i<6);

Yksi tyypillinen käyttö loppuehtoiselle toistolle on syöttötietojen tarkistaminen. Laaditaan sovellus Pariton.java, joka välttämättä haluaa parittoman luvun:

import java.util.Scanner;

public class Pariton {
  private static Scanner lukija = new Scanner(System.in);

  public static void main(String[] args) {
    int luku;
    boolean parillinen;

    do {
      System.out.println("Anna pariton luku!");
      luku = lukija.nextInt();
      parillinen = (luku%2 == 0);
      if (parillinen) 
        System.out.println("Eihän "+luku+" ole pariton ...");
    } while (parillinen);
    System.out.println("Hienoa, "+luku+" on pariton!");
  }
}
Ohjelman käyttö näyttää vaikkapa seuraavalta:
Anna pariton luku!
468
Eihän 468 ole pariton ...
Anna pariton luku!
-32
Eihän -32 ole pariton ...
Anna pariton luku!
9
Hienoa, 9 on pariton!

Huom: Totuusarvoisten muuttujien (eli loogisten eli boolean-muuttujuen) käyttäminen voi olla järkevää ja selkeää äskeisen esimerkin tapaan:

   boolean parillinen;
   ...
   parillinen = (luku%2 == 0);
      if (parillinen)
        ...
    } while (parillinen);
Totuusarvoiset muuttujat käyttäytyvät aivan kuin tutummat numeeriset muuttujat, arvoalue vain on suppea: true ja false. Loogisten lausekkeiden arvoja voidaan sijoittaa totuusarvoisille muuttujille ja totuusarvoisia muuttujia voidaan sellaisinaankin käyttää if-, while- ja do-lauseiden ehtona:
   int i = 7;
   double d = 3.14;
   boolean b, bb, bbb;

   b   = (i == 7) && (d > 10.0);  // false
   bb  = (i == 7) && (d < 10.0);  // true

   b   = (i == 7) || (d > 10.0);  // true
   bb  = (i != 7) || (d > 10.0);  // false

   bbb = !b || !!bb;              // false

   if (b)
      ...;
   while (bb) 
      ...;
   do
      ....;
   while (bbb);

Ohjelmaesimerkkejä: keskiarvo

Laaditaan kolme erilaista esimerkkisovellusta lukujen keskiarvon laskentaan. Ensimmäisessä käyttäjältä kysytään aluksi lukujen lukumäärä, toisessa lukujen loppuminen ilmaistaan ns. loppumerkillä, kolmas pyytää luvut yksitellen.

Esimmäinen ohjelmaversio (Karvo1.java) kysyy lukujen lukumäärän käyttäjältä.

import java.util.Scanner;

public class Karvo1 {
  private static Scanner lukija = new Scanner(System.in);

  public static void main(String[] args) {

    int lukujenLkm = 0;
    boolean lkmOk;

    double luku,
           lukujenSumma = 0,
           lukujenKarvo;

    System.out.println("\n**** Keskiarvon laskenta ****\n");

   // Monenko luvun keskiarvo lasketaan 
   // (tarkistetaan, että määrä ei ole negatiivinen):

    do {
      System.out.print("Monenko luvun keskiarvo lasketaan? ");
      lukujenLkm = lukija.nextInt();
      lkmOk = (lukujenLkm >= 0);   // nollakin kelpaa!
      if (!lkmOk)
        System.out.println("Virhe: negatiivinen ei kelpaa!");
    } while (!lkmOk); 


   // Lukujen summan laskenta:

    for (int monesko = 1; monesko <= lukujenLkm; ++monesko) {
      System.out.print("Anna "+monesko+". luku: ");
      luku = lukija.nextDouble();
      lukujenSumma += luku;
    }

  // Keskiarvon tulostus (estetään 0:lla jakaminen):

    if (lukujenLkm == 0)
      System.out.println("\nEi lukuja, ei keskiarvoa.\n");
    else {
      lukujenKarvo = lukujenSumma/lukujenLkm;
      System.out.println("\nKeskiarvo on "+lukujenKarvo+".\n");
    }
  }
}
Ohjelman suoritus voi näyttää seuraavanlaiselta:

**** Keskiarvon laskenta ****

Monenko luvun keskiarvo lasketaan? -23
Virhe: negatiivinen ei kelpaa!
Monenko luvun keskiarvo lasketaan? 3
Anna 1. luku: 40,1
Anna 2. luku: -3
Anna 3. luku: 9.1

Keskiarvo on 15.4.
 

Toinen ohjelmaversio (Karvo2.java) osaa laskea vain ei-negatiivisten lukujen keskiarvoja, mutta se on edellistä versiota joustavampi sikäli, että lukujen lukumäärää ei tarvitse tietää ennakolta. Ensimmäinen vastaantuleva negatiivinen luku on ohjelmalle ns. loppumerkki, jolla ohjelmalle ilmoitetaan, että syöttölukuja ei enää anneta lisää. Loppumerkki itse ei enää kuulu lukuihin, joiden keskiarvo lasketaan.
import java.util.Scanner;

public class Karvo2 {
  private static Scanner lukija = new Scanner(System.in);

  public static void main(String[] args) {

    int lukujenLkm = 0;

    double luku,
           lukujenSumma = 0,
           lukujenKarvo;

    System.out.println("\n**** Keskiarvon laskenta ****\n");

   // Käyttöohjeen tulostus:

    System.out.println("Syötä luvut, joiden keskiarvon haluat laskea!\n"+
                        "Negatiivinen luku ilmoittaa lukujen loppuvan.\n");


   // Lukujen summan laskenta:

    luku = lukija.nextDouble();      // 1. luku (voi olla jo loppumerkki!)
    while (luku >= 0) {
      lukujenSumma += luku;
      ++lukujenLkm;
      luku = lukija.nextDouble();    // seuraava luku tai loppumerkki
    }

  // Keskiarvon tulostus (estetään 0:lla jakaminen):

    if (lukujenLkm == 0)
      System.out.println("\nEi lukuja, ei keskiarvoa.\n");
    else {
      lukujenKarvo = lukujenSumma/lukujenLkm;
      System.out.println("\nKeskiarvo on "+lukujenKarvo+".\n");
    }
  }
}
Ohjelman käyttö näyttää vaikkapa seuraavanlaiselta:

**** Keskiarvon laskenta ****

Syötä luvut, joiden keskiarvon haluat laskea!
Negatiivinen luku ilmoittaa lukujen loppuvan.

21,2
0,02
33
2,5
-1

Keskiarvo on 14.18.
 

Kolmas ohjelmaversio (Karvo3.java) kysyy ennen jokaista syöttölukua, halutaanko lukuja vielä syöttää. Kysymykseen vastaaminen on tässä esimerkissä vielä kömpelöä, koska merkkijonojen vertailua ei vielä ole opittu.
import java.util.Scanner;

public class Karvo3 {
  private static Scanner lukija = new Scanner(System.in);

  public static void main(String[] args) {

    int lukujenLkm = 0,
        jatkuu;

    double luku,
           lukujenSumma = 0,
           lukujenKarvo;

    System.out.println("\n**** Keskiarvon laskenta ****\n");

   // Lukujen summan laskenta:

    do {
      System.out.println("Onko vielä lukuja? (1:on, muu luku: ei)");
      jatkuu = lukija.nextInt();
      if (jatkuu == 1) {
        System.out.println("Anna " + (lukujenLkm + 1) + ". luku");
        luku = lukija.nextDouble();
        lukujenSumma += luku;
        ++lukujenLkm;
      }
    } while (jatkuu == 1);

  // Keskiarvon tulostus (estetään 0:lla jakaminen):

    if (lukujenLkm == 0)
      System.out.println("\nEi lukuja, ei keskiarvoa.\n");
    else {
      lukujenKarvo = lukujenSumma/lukujenLkm;
      System.out.println("\nKeskiarvo on "+lukujenKarvo+".\n");
    }
  }
}

Ohjelman suoritus voi näyttää vaikkapa seuraavalta:
**** Keskiarvon laskenta ****

Onko vielä lukuja? (1:on, muu luku: ei)
1
Anna 1. luku
76
Onko vielä lukuja? (1:on, muu luku: ei)
1
Anna 2. luku
-4
Onko vielä lukuja? (1:on, muu luku: ei)
0

Keskiarvo on 36.0.

Luvussa 2.7 opitaan, miten vastaus voitaisiin toteuttaa String-arvona (Karvo4.java):
    ...
    String jatkuu;
    ...
    do {
      System.out.println("Onko vielä lukuja? (kyllä/ei)");

      jatkuu = lukija.next();

      if (jatkuu.equals("kyllä")) {
        System.out.println("Anna " + (lukujenLkm + 1) + ". luku");
        luku = lukija.nextDouble();
        lukujenSumma += luku;
        ++lukujenLkm;
      }
    } while (jatkuu.equals("kyllä"));
    ...

Suoritusesimerkki:
Onko vielä lukuja? (kyllä/ei)
kyllä
Anna 1. luku
314
Onko vielä lukuja? (kyllä/ei)
kyllä
Anna 2. luku
51
Onko vielä lukuja? (kyllä/ei)
kyllä
Anna 3. luku
-23
Onko vielä lukuja? (kyllä/ei)
ei

Keskiarvo on 114.0.

Huom: Viimeisessä esimerkissä lause jatkuu = lukija.nextLine(); ei toimisi oikein! Kokele miten käy! Mistä voisi olla kysymys?
Vastaus: Operaatiot nextInt ja nextDouble eivät lue rivin loppumerkkiä, joka siis jää syöttöpuskuriin. Tässä tilanteessa nextLine palauttaa arvonaan luvun ja rivinlopun väliin jääneen merkkijonon, joka voi olla myös tyhjä eli "".

Syöttötietojen tarkistamisesta

Luvussa 2.3 opittiin tietojen lukemista metodein Jos esimerkiksi nextDouble-operaatiolle syötetään desimaaliluvuksi kelpaamaton arvo, ohjelman suoritus keskeytyy seuraavantapaiseen virheilmoitukseen:
Exception in thread "main" java.util.InputMismatchException
        at java.util.Scanner.throwFor(Scanner.java:819)
        at java.util.Scanner.next(Scanner.java:1431)
        at java.util.Scanner.nextDouble(Scanner.java:2335)
        at KolmeKarvo.main(KolmeKarvo.java:14)

Tällainen ei tietenkään ole oikeissa ohjelmissa hyväksyttävää! Niissä syöttötietojen oikeellisuus pitää siis tarkistaa. Tämän luvun oppien jälkeen se onkin mahdollista: tässäkin tapauksessa loogisista lausekkeista, ehdollisista lauseista ja toistolauseista on iloa.

Scanner-luokassa on joukko totuusarvoisia eli boolean-tyyppisiä metodeita seuraavaksi luettavan syöttöalkion tyypin tutkimiseen, muiden muassa:

Näillä välineillä voidaan tarkistaa esimerkiksi, onko seuraava syöttöalkio kokonaisluku (Tarkista1.java):

        int luku;

        System.out.println("Anna kokonaisluku.");

        if (lukija.hasNextInt()) {
            luku = lukija.nextInt();
            System.out.println("Annoit luvun " + luku);

        } else {
            System.out.println("Et syöttänyt kokonaislukua!");
        }

Toki voidaan myös vaatia kunnon kokonaisluku (Tarkista2.java):

        int luku = 0;   // kääntäjän iloksi!
        boolean kunnonLuku;

        do {
          System.out.println("Anna kokonaisluku.");
          kunnonLuku = lukija.hasNextInt();
          if (kunnonLuku)
             luku = lukija.nextInt();
          else {
            String väärä = lukija.next();  // ohitetaan kelvoton alkio
            System.out.print  (väärä + " ei ole kokonaisluku! ");
            System.out.println("Yritä uudelleen!");
          }
        } while (!kunnonLuku);

        System.out.println("Annoit luvun " + luku);

Koska tällä kurssilla harjoitellaan ohjelmoinnin perusvälineistöä, useinkaan ei ole syytä käyttää voimia ja ohjelmarivejä syöttötietojen tarkastamiseen, jottei opeteltava uusi asia hukkuisi muun ohjelmatekstin sisään. Jos ja kun harjoitus- ja koetehtävissä syötteiden tarkistamista vaaditaan, se sanotaan erikseen.

On kuitenkin syytä pitää mielessä se jo useampaan kertaan todettu sääntö, että "oikeissa" käyttäjille tarkoitetuissa ohjelmissa syöttötiedot tarkistetaan aina!

Ohjelmointiesimerkki: suurin syöttöluku

Laadi ohjelma, joka etsii syöttöluvuista suurimman. Nolla on loppumerkki.

Ratkaisu (SuurinSyottoluku.java):

import java.util.Scanner;

public class SuurinSyottoluku {
  private static Scanner lukija = new Scanner(System.in);

  public static void main(String[] args) {

    int syöttöluku,
        toistaiseksiSuurin = 0;  // muuttuu aina, jos on lukuja

   
    System.out.println("************************************************\n" +
                       "* Ohjelma syöttölukujen maksimiarvon etsintään *\n" +
                       "*        Nolla päättää syötteet.               *\n" +
                       "*        Anna luvut!                           *\n" +
                       "************************************************\n");

    syöttöluku = lukija.nextInt();
    toistaiseksiSuurin = syöttöluku;

    while (syöttöluku != 0) {
      if (syöttöluku > toistaiseksiSuurin)
        toistaiseksiSuurin = syöttöluku;
      syöttöluku = lukija.nextInt();
    }

    if (toistaiseksiSuurin == 0)
      System.out.println("Ei lukuja!");
    else
      System.out.println("Suurin luku oli " + toistaiseksiSuurin);
  }
}


Takaisin luvun 2 sisällysluetteloon.