Arto Wikla 2011, Materiaalia saa vapaasti käyttää itseopiskeluun. Muu käyttö vaatii luvan.

Ohjelmoinnin jatkokurssi: harjoitukset s2011: 1/6 (31.10.-4.11.)

(Muutettu viimeksi 30.10.2011, sivu perustettu 25.10.2011.)

Nämä harjoitukset liittyvät lähinnä kurssimateriaalin lukuihin 2-6.

Kaikki harjoitustehtävät on syytä tehdä. Jotkin tehtävät on merkitty keltaisella värillä. Ne ovat ehkä hieman muita haastavampia. Ilman niitäkin harjoituksista voi saada maksimipisteet, mutta ne lasketaan silti mukaan harjoituspisteitä määrättäessä – ne voivat siis korvata joitakin haasteettomampia tehtäviä tms. Mutta ennen kaikkea noista keltaisista tehtävistä sitä vasta oppiikin!

Huom:

Sormiharjoituksia

Osallistujat.java

Laadi luentomateriaalin ArrayList-esimerkkiä jäljitellen seuraava palvelu: Aluksi ohjelma kyselee joukon "osallistujien" nimiä standardisyöttövirrasta. Tyhjä merkkijono päättää nimien syötön. Tämän jälkeen ohjelma tarjoaa varsinaisen palvelunsa: Ohjelmalle syötetään nimi ja se tulostaa standarditulosvirtaan joko tiedon, ettei "osallistujaa" löydy tai "osallistujan" nimen ja hänen järjestysnumeronsa eli tiedon siitä, monentenako nimi syötettiin. Tyhjä nimi päättää ohjelman suorituksen.

Suoritusesimerkki (käyttäjän kirjoittamat tekstit tässä kursiivilla):

Anna osallistujan nimi. Tyhjä lopettaa.
Jyrki
Anna osallistujan nimi. Tyhjä lopettaa.
Jutta
Anna osallistujan nimi. Tyhjä lopettaa.
Stefan
Anna osallistujan nimi. Tyhjä lopettaa.
Paavo
Anna osallistujan nimi. Tyhjä lopettaa.
Päivi
Anna osallistujan nimi. Tyhjä lopettaa.
Heidi
Anna osallistujan nimi. Tyhjä lopettaa.

Osallistujakysely. Tyhjä lopettaa.
Jutta
Jutta: 2
Mari
Mari puuttuu!
Heidi
Heidi: 6
Timo
Timo puuttuu!

Kehitysversio edellisestä

Ohjelman ensiversio sallii saman nimen syöttämisen useampaan kertaan. Korjaa tämä puute. Tarjoa lisäksi kyselyn yhteydessä mahdollisuus lisätä puuttuva "osallistuja" ja poistaa löytynyt "osallistuja".

Suoritusesimerkki (käyttäjän kirjoittamat tekstit tässä kursiivilla):

Anna osallistujan nimi. Tyhjä lopettaa.
Jyrki
Anna osallistujan nimi. Tyhjä lopettaa.
Jutta
Anna osallistujan nimi. Tyhjä lopettaa.
Stefan
Anna osallistujan nimi. Tyhjä lopettaa.
Jyrki
On jo!
Anna osallistujan nimi. Tyhjä lopettaa.

Osallistujakysely. Tyhjä lopettaa.
Mari
Mari puuttuu! Lisätäänkö? (tyhjä = ei, kaikki muut = kyllä)

Osallistujakysely. Tyhjä lopettaa.
Paavo
Paavo puuttuu! Lisätäänkö? (tyhjä = ei, kaikki muut = kyllä)
joo
Osallistujakysely. Tyhjä lopettaa.
Jyrki
Jyrki: 1. Poistetaanko? (tyhjä = ei, kaikki muut = kyllä)
ok
Osallistujakysely. Tyhjä lopettaa.
Jutta
Jutta: 1. Poistetaanko? (tyhjä = ei, kaikki muut = kyllä)

Osallistujakysely. Tyhjä lopettaa.

Tavarat ja matkalaukku

Tässä tehtäväsarjassa tehdään luokat Tavara ja Matkalaukku, joiden avulla käsitellään matkalaukussa olevia tavaroita.

Oikeastaan kahdessa ensimmäisessä tehtäväsarjassa harjoitellaan ensiksikin sitä, miten luokkamäärittelyn kenttä itse voi olla viittaustyyppinen eli miten "oliossa voi olla olioita". Lisäksi harjoitellaan eritysesti sitä, miten "olio voi sisältää" vieläpä vaihtelevan määrän olioita – "matkalaukussa tavaroita" ja "lastiruumassa matkalaukkuja".

Tavara-luokka

Tee luokka Tavara, josta muodostetut oliot vastaavat erilaisia tavaroita. Tallennettavat tiedot ovat tavaran nimi ja paino (kg).

APIn luonnehdinta:

Käyttöesimerkki:

public class Main {
  public static void main(String[] args) {
    Tavara kirja = new Tavara("Aapiskukko", 2);
    Tavara puhelin = new Tavara("Nokia 3210", 1);

    System.out.println("Kirjan nimi: " + kirja.getNimi());
    System.out.println("Kirjan paino: " + kirja.getPaino());

    System.out.println("Kirja: " + kirja);
    System.out.println("Puhelin: " + puhelin);
  }
}

Ohjelman tulostuksen tulisi olla seuraava:

Kirjan nimi: Aapiskukko
Kirjan paino: 2
Kirja: Aapiskukko (2 kg)
Puhelin: Nokia 3210 (1 kg)

Matkalaukku-luokka

Tee luokka Matkalaukku, johon liittyy maksimipaino.

APIn luonnehdinta:

Tallenna tavarat seuraavaan ArrayList-rakenteeseen:

ArrayList<Tavara> tavarat = new ArrayList<Tavara>();

Luokan Matkalaukku tulee valvoa, että sen tavaroiden yhteispaino ei ylitä maksimipainoa. Jos maksimipaino ylittyisi uuden tavaran vuoksi, metodi lisaaTavara ei lisää tavaraa matkalaukkuun.

Käyttöesimerkki:

public class Main {
  public static void main(String[] args) {
    Tavara kirja = new Tavara("Aapiskukko", 2);
    Tavara puhelin = new Tavara("Nokia 3210", 1);
    Tavara tiiliskivi = new Tavara("tiiliskivi", 4);

    Matkalaukku matkalaukku = new Matkalaukku(5);
    System.out.println(matkalaukku);

    matkalaukku.lisaaTavara(kirja);
    System.out.println(matkalaukku);

    matkalaukku.lisaaTavara(puhelin);
    System.out.println(matkalaukku);

    matkalaukku.lisaaTavara(tiiliskivi);
    System.out.println(matkalaukku);
  }
}

Ohjelman tulostuksen tulisi olla seuraava:

0 tavaraa (0 kg)
1 tavaraa (2 kg)
2 tavaraa (3 kg)
2 tavaraa (3 kg)

Kielenhuoltoa

Ilmoitukset "0 tavaraa" ja "1 tavaraa" eivät ole kovin hyvää suomea – paremmat muodot olisivat "tyhjä" ja "1 tavara". Tee tämä muutos luokkaan Matkalaukku.

Nyt edellisen ohjelman tulostuksen tulisi olla seuraava:

tyhjä (0 kg)
1 tavara (2 kg)
2 tavaraa (3 kg)
2 tavaraa (3 kg)

Kaikki tavarat

Lisää luokkaan Matkalaukku seuraavat metodit:

Käyttöesimerkki:

public class Main {
  public static void main(String[] args) {
    Tavara kirja = new Tavara("Aapiskukko", 2);
    Tavara puhelin = new Tavara("Nokia 3210", 1);
    Tavara tiiliskivi = new Tavara("tiiliskivi", 4);

    Matkalaukku matkalaukku = new Matkalaukku(10);
    matkalaukku.lisaaTavara(kirja);
    matkalaukku.lisaaTavara(puhelin);
    matkalaukku.lisaaTavara(tiiliskivi);

    System.out.println("Matkalaukussa on seuraavat tavarat:");
    matkalaukku.tulostaTavarat();
    System.out.println("Yhteispaino: " + matkalaukku.yhteispaino() + " kg");
  }
}

Ohjelman tulostuksen tulisi olla seuraava:

Matkalaukussa on seuraavat tavarat:
Aapiskukko (2 kg)
Nokia 3210 (1 kg)
Tiiliskivi (4 kg)
Yhteispaino: 7 kg

Raskain tavara

Lisää vielä luokkaan Matkalaukku metodi raskainTavara, joka palauttaa painoltaan suurimman tavaran. Jos yhtä raskaita tavaroita on useita, metodi voi palauttaa minkä tahansa niistä. Metodin palauttaa siis viitteen olioon.

Käyttöesimerkki:

public class Main {
  public static void main(String[] args) {
    Tavara kirja = new Tavara("Aapiskukko", 2);
    Tavara puhelin = new Tavara("Nokia 3210", 1);
    Tavara tiiliskivi = new Tavara("tiiliskivi", 4);

    Matkalaukku matkalaukku = new Matkalaukku(10);
    matkalaukku.lisaaTavara(kirja);
    matkalaukku.lisaaTavara(puhelin);
    matkalaukku.lisaaTavara(tiiliskivi);

    Tavara raskain = matkalaukku.raskainTavara();
    System.out.println("Raskain tavara: " + raskain);
  }
}

Ohjelman tulostuksen tulisi olla seuraava:

Raskain tavara: tiiliskivi (4 kg)

Satunnainen tavara

Lisää luokkaan Matkalaukku metodi poimiTavara, joka palauttaa matkalaukusta satunnaisen tavaran. Metodin ei ole tarkoitus poistaa tavaraa matkalaukusta, vaan ainoastaan palauttaa viite satunnaisesti valittuun Tavara-olioon.

Käyttöesimerkki:

public class Main {
  public static void main(String[] args) {
    Tavara kirja = new Tavara("Aapiskukko", 2);
    Tavara puhelin = new Tavara("Nokia 3210", 1);
    Tavara tiiliskivi = new Tavara("tiiliskivi", 4);

    Matkalaukku matkalaukku = new Matkalaukku(10);
    matkalaukku.lisaaTavara(kirja);
    matkalaukku.lisaaTavara(puhelin);
    matkalaukku.lisaaTavara(tiiliskivi);

    Tavara satunnainen = matkalaukku.poimiTavara();
    System.out.println("Satunnainen tavara: " + satunnainen);
  }
}

Ohjelman mahdollisia tulostuksia ovat seuraavat:

Satunnainen tavara: Aapiskukko (2 kg)
Satunnainen tavara: Nokia 3210 (1 kg)
Satunnainen tavara: tiiliskivi (4 kg)

Kapselointi – mitä, kuka, häh?

Selitä ohjaajalle miten käsite kapselointi liittyy tehtäväsarjassa 3 toteuttamaasi ratkaisuun.

Lastiruuma

Tässä tehtäväsarjassa tehdään luokka Lastiruuma, joka esittää matkalaukkuja sisältävää lastiruumaa.

Lastiruuma-luokka

Tee luokka Lastiruuma. APIn luonnehdinta:

Tallenna matkalaukut sopivaan ArrayList-rakenteeseen.

Luokan Lastiruuma tulee valvoa, että sen matkalaukkujen yhteispaino ei ylitä maksimikapasiteettia. Jos maksimikapasiteetti ylittyisi uuden matkalaukun vuoksi, metodi lisaaMatkalaukku ei lisää matkalaukkua.

Käyttöesimerkki:

public class Main {
  public static void main(String[] args) {
    Tavara kirja = new Tavara("Aapiskukko", 2);
    Tavara puhelin = new Tavara("Nokia 3210", 1);
    Tavara tiiliskivi = new Tavara("tiiliskivi", 4);

    Matkalaukku matinLaukku = new Matkalaukku(10);
    matinLaukku.lisaaTavara(kirja);
    matinLaukku.lisaaTavara(puhelin);

    Matkalaukku pekanLaukku = new Matkalaukku(10);
    pekanLaukku.lisaaTavara(tiiliskivi);

    Lastiruuma lastiruuma = new Lastiruuma(1000);
    lastiruuma.lisaaMatkalaukku(matinLaukku);
    lastiruuma.lisaaMatkalaukku(pekanLaukku);

    System.out.println(lastiruuma);
  }
}

Ohjelman tulostuksen tulisi olla seuraava:

2 matkalaukkua (7 kg)

Kaikki tavarat

Lisää luokkaan Lastiruuma metodi tulostaTavarat, joka tulostaa kaikki lastiruuman matkalaukuissa olevat tavarat.

Käyttöesimerkki:

public class Main {
  public static void main(String[] args) {
    Tavara kirja = new Tavara("Aapiskukko", 2);
    Tavara puhelin = new Tavara("Nokia 3210", 1);
    Tavara tiiliskivi = new Tavara("tiiliskivi", 4);

    Matkalaukku matinLaukku = new Matkalaukku(10);
    matinLaukku.lisaaTavara(kirja);
    matinLaukku.lisaaTavara(puhelin);

    Matkalaukku pekanLaukku = new Matkalaukku(10);
    pekanLaukku.lisaaTavara(tiiliskivi);

    Lastiruuma lastiruuma = new Lastiruuma(1000);
    lastiruuma.lisaaMatkalaukku(matinLaukku);
    lastiruuma.lisaaMatkalaukku(pekanLaukku);

    System.out.println("Ruuman matkalaukuissa on seuraavat tavarat:");
    lastiruuma.tulostaTavarat();
  }
}

Ohjelman tulostuksen tulisi olla seuraava:

Ruuman matkalaukuissa on seuraavat tavarat:
Aapiskukko (2 kg)
Nokia 3210 (1 kg)
tiiliskivi (4 kg)

Paljon tiiliskiviä

Testataan vielä, että lastiruuman toiminta on oikea eikä maksimipaino pääse ylittymään. Tee ohjelma, joka lisää lastiruumaan 100 matkalaukkua, joissa jokaisessa on yksi tiiliskivi. Tiiliskivien painot ovat 1, 2, 3, ..., 100 kg.

Ohjelman runko on seuraava:

public class Main {
  public static void main(String[] args) {
    Lastiruuma lastiruuma = new Lastiruuma(1000);
    // 100 matkalaukun lisääminen
    System.out.println(lastiruuma);
  }
}

Ohjelman tulostuksen tulisi olla seuraava:

44 matkalaukkua (990 kg)

Mitä tietorakenteen "dynaamisuus" oikein onkaan

Kun jokin ohjelmointikielen väline vaikuttaa helppokäyttöiseltä ja joustavalta, kyseessä on se, että joku on toteuttanut eli ohjelmoinut, tuon helppouden! Eikä se välttämättä ole ollut helppoa...

Luokassa String on monenlaisia välineitä merkkijonojen käsittelyyn. Kerran luotu String-olio tunnetusti ei kuitenkaan voi koskaan muuttua – String-oliot ovat kerran synnyttyään muuttumattomia, "immutaabeleita".

Tässä tehtäväsarjassa toteutetaan vaiheittain merkkijonoluokka MyString muutettavien merkkijononojen toteutukseksi. Tehtävienn ratkaisemisessa on pyrittävä välttämään roskienkerääjän ylensyöttämistä. Itse asiassa Java-APIssa on olemassa luokat StringBuilder ja StringBuffer muutettavien merkkijonojen käsittelyyn, mutta asioiden tekeminen omin käsin on opettavaista! Näiden tehtävien ratkaisussa ei saa käyttää StringBuilder- ja StringBuffer-olioita eikä aluksi myöskään ArrayList-olioa. Myöskään – sinänsä ovelaa ideaa! &ndash muuntaa merkkitaulukko String-olioksi ja toteuttaa kaikki MyString-operaatiot String-operaatioin ei saa käyttää, koska silloin ruokittaisiin aivan tarpeettomasti roskienkerääjää. (Ymmärsitkö äskeisen lauseen? Hienoa ja hyvä merkki, jos ymmärsit!)

MyString, vaihe 1

MyString-luokka kapseloi sisäänsä merkkitaulukon, jonka alkuosassa säilytetään esitettävää merkkijonoa. Luokan määrittely alkaa seuraavasti:

public class MyString {

  public static final int MAKSIMIPITUUS = 100; // - pisin mahdollinen merkkijono
                                               // - näkyy kaikkialle ilmauksella: MyString.MAKSIMIPITUUS

  private char[] mjono; // - merkkijononon esitys char-taulukkona
  private int pituus;   // - montako alkioita mjono:n alusta on käytössä
  ...                   //   eli ensimmäisen käyttämättömän alkion indeksi
                        // - tyhjässä MyString-oliossa arvo on siis 0
}

Määrejono public static final määrää, että kenttä MAKSIMIPITUUS näkyy kaikkialle, että se liittyy luokkaan, ei olioon, ja että sen arvoa ei voi muuttaa. Tällaiset nimetyt vakiot on tapana kirjoittaa isoin kirjaimin.

Toteutetaan ensin seuraava hyvin suppea API:

Kokeile ja esittele MyStringin käyttöä pienellä ohjelmalla.

MyString, vaihe 2

Täydennä MyStringin toteutusta seuraavilla välineillä:

Kokeile ja esittele kehitellyn MyStringin käyttöä pienellä ohjelmalla.

MyString, vaihe 3

Täydennä MyStringin toteutusta seuraavilla välineillä:

Kokeile ja esittele kehitellyn MyStringin käyttöä pienellä ohjelmalla.

MyString, vaihe 4

Täydennä MyStringin toteutusta String-luokasta tutuin aksessorein compareTo ja indexOf(char). Poiketen String luokan compareTo-metodista riittää, että MyStringin compareTo-metodi palauttaa vain jonkin arvoista -1, 0 tai 1.

Kokeile ja esittele kehitellyn MyStringin käyttöä pienellä ohjelmalla.

MyString, vaihe 5

Täydennä MyStringin toteutusta String-luokasta tutulla aksessorilla indexOf(MyString).

Kokeile ja esittele kehitellyn MyStringin käyttöä pienellä ohjelmalla.

MyString, vaihe 6

Toteuta vähintään vaiheen 3 (tämän sarjan 3. tehtävä) vahvuinen MyString siten, että piilossa pidetty tietorakenne ei olekaan char-taulukko vaan ArrayList<Character>-olio. Voit samalla luopua MyString-olion pituusrajoituksesta. Ja jos nyt tälle tielle lähdit, mikset toteuttasi vaiheen 5 versiotakin. ;-) Ei ole pakko...