Lisamaterjalid 5
Võtmesõna record
Javas klassi kirjutades on tihti vaja defineerida isendivälju, konstruktorit, get
- ja set
-meetodeid ja toString()
. Kui klassi eesmärk on andmete hoidmine ja mitte nende muutmine, siis selleks saab kasutada võtmesõna record
. Selles on vaikimisi loodud isendiväljad, konstruktor, get
-meetodid, toString()
, equals
ja hashCode()
. Üheks piiranguks on see, et isendiväljad on final
ehk nende väärtusi ei saa hiljem muuta. Sellepärast ei ole lubatud isendiväljadele luua set
-meetodeid. Lisaks saab record
-is väljaspool meetodeid deklareerida ainult klassimuutujaid.
Tass1
on algeline variant record
-ist, millel on kaks argumenti maht ja värv ning ülejäänud on vaikimisi genereeritud:
record Tass1(int maht, String värv) { }
Loome Tass1
isendi mustTass
. Sellel on kaks get
-meetodit maht()
ja värv()
, mis vastavad isendiväljade nimedele. Objekti sõneks tegemiseks on meetod toString()
ja räsi teadasaamiseks hashCode()
:
Tass1 mustTass = new Tass1(200, "must"); System.out.println("Tassi maht on " + mustTass.maht() + " ml ja tassi värv on " + mustTass.värv()); System.out.println(mustTass); System.out.println(mustTass.hashCode());
Väljund:
Tassi maht on 200 ml ja tassi värv on must Tass1[maht=200, värv=must] 3369537
Isendite võrdlemiseks on olemas meetod equals
. Võrdleme varem loodud mustTass
-i uute isenditega valgeTass
ja kaMustTass
:
Tass1 valgeTass = new Tass1(200, "valge"); Tass1 kaMustTass = new Tass1(200, "must"); System.out.println(mustTass.equals(valgeTass)); System.out.println(mustTass.equals(kaMustTass));Väljund:
false true
Kõiki vaikimisi loodud meetodeid saab ise defineerida. Klassi Tass2
-e konstruktor muudab negatiivse mahu nulliks ja toString()
tagastab kohandatud sõne mahust ja värvist. Lisaks on loodud meetod poolMahust()
, mis tagastab mahu kahega jagamise tulemuse.
record Tass2(int maht, String värv) { public Tass2(int maht, String värv) { if (maht < 0) { this.maht = 0; } else { this.maht = maht; } this.värv = värv; } public double poolMahust() { return maht/2.0; } public String toString() { return "Tassi maht on " + maht + " ml ja värv on " + värv; } }
Loome isendi negatiivneTass
ja väljastame selle toString()
tagastuse:
Tass2 negatiivneTass = new Tass2(-350, "kollane"); System.out.println(negatiivneTass);Väljund:
Tassi maht on 0 ml ja värv on kollane
Proovime meetodi poolMahust()
hallTass
-iga:
Tass2 hallTass = new Tass2(51, "hall"); System.out.println(hallTass.poolMahust());Väljund:
25.5
record
-il on olemas kompaktne konstruktor (compact constructor), mis on lühem variant tavalisest konstruktorist. Selles ei kirjutata välja parameetreid ning väärtustamine toimub automaatselt. Vajadusel saab isendiväljade algseid väärtusi ise defineerida.
record Tass2Lühem(int maht, String värv) { public Tass2Lühem { if (maht < 0) { maht = 0; } } public double poolMahust() { return maht/2.0; } public String toString() { return "Tassi maht on " + maht + " ml ja värv on " + värv; } }
Klass Tass2Lühem
töötab samamoodi nagu Tass2
:
Tass2Lühem negatiivneTassLühem = new Tass2Lühem(-350, "kollane"); System.out.println(negatiivneTassLühem);Väljund:
Tassi maht on 0 ml ja värv on kollane
record
-is on argumentideks antud isendiväljad varustatud piiritlejatega private final
. Kuna nende väljade väärtusi ei saa muuta, siis ei tohi neile luua set
-meetodeid. Järgnev koodijupp viskab kompileerimisel veateate:
record Tass3(int maht, String värv) { public void setMaht(int maht) { //keelatud this.maht = maht; } }
Lisaks isendiväljadele saab luua klassimuutujaid, mille defineerimiseks kasutatakse võtmesõna static
. Klassis Tass4
on String
-tüüpi static
muutuja värvid
. Sellel on get
-meetod getVärvid()
ja set
-meetod setVärvid
, mida kutsutakse välja konstruktoris, et lisada sõnesse värve.
record Tass4(int maht, String värv) { private static String värvid; public Tass4 { setVärvid(värv); } public static String getVärvid() { return värvid; } public static void setVärvid(String värv) { if (värvid == null) { värvid = värv; } else { värvid = värvid + "; " + värv; } } }
Alguses on muutuja värvid väärtus null. Kui loome isendi rohelineTass
, siis on muutuja väärtuseks "roheline"
. Luues isendi lillaTass
lisatakse "lilla"
värvidesse. Lõpuks kutsume välja meetodi setVärvid
läbi klassi, mis lisab värvidesse sõne "must"
:
Tass4 rohelineTass = new Tass4(100, "roheline"); System.out.println(Tass4.getVärvid()); Tass4 lillaTass = new Tass4(100, "lilla"); System.out.println(Tass4.getVärvid()); Tass4.setVärvid("must"); System.out.println(Tass4.getVärvid());
Väljund:
roheline roheline; lilla roheline; lilla; must
Enesekontroll
Iteraator
Varem tutvustatud ArrayList
, LinkedList
, HashMap
ja paljud teised andmestruktuurid on kogumid (collections). Nende läbimiseks saab kasutada liidest
java.util.Iterator
, mille isendeid luuakse liidese
java.lang.Iterable
meetodi iterator()
abil. Iga kogum ei realiseeri seda meetodit otse. Näiteks klassil HashMap
ei ole meetodi iterator()
, aga sellel on meetod values()
. See tagastab
Collection
-i, millel on iteraatori loomise meetod. Iteraatoril on neli meetodit:
next()
– tagastab iteratsiooni järgmise elemendi (vt meetod1);hasNext()
– tagastab tõeväärtuse sellest, kas iteratsioonis on veel läbimata elemente (vt meetod1);remove()
– eemaldab hetkel vaadeldava elemendi (vt meetod2);forEachRemaining
– käib läbi iga elemendi ja kasutab neid ise defineeritud viisil (vt meetod3).
Iteraatoril on paar eelist. Sellega saab murevabalt kogumist elemente eemaldada. for
-tsüklis peab vastavalt muutma indeksi väärtust, et ükski element ei jääks vahele. for
-each tsüklis visatakse tihti erind ConcurrentModificationException
, kui proovitakse mitut elementi eemaldada. Teiseks on iteraator universaalsem viis kogumis elementide läbimiseks, sest näiteks Set
ja Queue
ei toeta tavalist for
-tsüklit.
Samas võib iteraatorit kasutades kergemini vigu tekkida. for
-tsüklis kasutatakse indeksit, mida võib ühes iteratsioonis elemendi tagastamiseks mitu korda kasutada. Iteraator kasutab selleks meetodit next()
, mida ei ole turvaline mitu korda välja kutsuda samas iteratsioonis, sest iga kutse tagastab uue elemendi. Lisaks saab indeksiga vaadata ükskõik millist elementi suvalisel hetkel. Iteraator peab läbima eelnevad elemendid, et jõuda õige juurde.
Esimene meetod meetod1
kasutab iteraatorit, et läbida listi ja selle käigus väljastatakse iga element. Argumendiks on Integer
-idest koosnev list, mis muudetakse iteraatoriks meetodiga iterator()
. Järgmise elemendi saamiseks kasutatakse meetodi next()
. Seda kutsutakse välja while
-tsüklis, kus kontrollitakse hasNext()
abil, kas läbikäimata listi elemente eksisteerib.
public static void meetod1(List<Integer> list) { Iterator<Integer> listiIter = list.iterator(); while (listiIter.hasNext()) { System.out.println(listiIter.next()); } }
Näide meetod1
tööst:
List<Integer> listNumbrid1 = new ArrayList(Arrays.asList(1, 2, 3, 4)); meetod1(listNumbrid1);
Väljastab:
1 2 3 4
Järgmine meetod meetod2
eemaldab listist kõik arvud, mis on negatiivsed või jaguvad viiega. Selleks, et igas tsükli iteratsioonis ei peaks meetodi next()
mitu korda tingimuste kontrollimiseks välja kutsuma, salvestame selle väärtuse muutujasse arv. Elemendi eemaldamiseks kasutatakse meetodi remove()
.
public static void meetod2(List<Integer> list) { Iterator<Integer> listiIter = list.iterator(); while (listiIter.hasNext()) { int arv = listiIter.next(); if (arv < 0 || arv % 5 == 0) { listiIter.remove(); } } }
Siin on näited, kus listist eemaldatakse negatiivsed või viiega jaguvad arvud:
List<Integer> listNumbrid2 = new ArrayList<>(Arrays.asList(1, -3, -15, 9, 100, 40)); meetod2(listNumbrid2);
Listist eemaldati kõik numbrid peale 1 ja 9.
List<Integer> listNumbrid2AsList = Arrays.asList(7, 3, 10); meetod2(listNumbrid2AsList);
See viskab erindi, sest Arrays.asList()
loob fikseeritud suurusega listi ja meetod2
proovib muuta selle suurust.
meetod3
liidab igale listi elemendile arvu 1000 ja väljastab muudetu. Iteraatorit saab ka läbida meetodiga forEachRemaining
, mis käib läbi iga elemendi ja teeb nendega midagi. Sel juhul toimub arvu tuhande võrra suurendamine ja väljastamine. Listi sees numbrid ei muutu.
public static void meetod3(List<Integer> list) { list.iterator().forEachRemaining(number -> { number += 1000; System.out.println(number); }); }
Igale arvule liidetakse 1000 ja tulemus väljastatakse:
List<Integer> listNumbrid3 = Arrays.asList(15, 5, 25); meetod3(listNumbrid3);
Väljastab:
1015 1005 1025
Listi väärtused ei muutunud:
System.out.println(listNumbrid3);Väljastab:
[15, 5, 25]
List
-il on eriline meetod listIterator()
, mille kaudu saab kasutada liidese java.util.ListIterator
meetodeid. Sellel on peale java.util.Iterator
meetodite veel:
nextIndex()
– tagastab järgmise elemendi indeksi;previous()
– tagastab iteratsiooni eelmise elemendi;hasPrevious()
– tagastab tõeväärtuse sellest, kas iteratsioonis on tagasi minnes elementi, mida vaadata;previousIndex()
– tagastab eelmise elemendi indeksi;add
– lisab uue elemendi vaadeldavast elemendist järgmisele kohale;set
– asendab vaadeldava elemendi uuega.
Need meetodid võimaldavad liste läbida tagurpidi ja lisaks muuta või lisada elemente.
Meetod meetod4
lisab iteraatoriga iga arvu paremale sama väärtusega elemendi. Neid lisatakse meetodi add
abil. Lisaks läbitakse listi liikmed tagurpidi ja selle käigus vähendatakse nende väärtusi ühe võrra. Elemendi asendamiseks sobib meetod set
ja paremalt vasakule liikumiseks on meetod previous()
, mis tagastab eelneva elemendi. Sellise elemendi olemasolu kontrollimiseks kasutatakse meetodi hasPrevious()
.
public static void meetod4(List<Integer> list) { ListIterator<Integer> listiIter = list.listIterator(); while (listiIter.hasNext()) { int arv = listiIter.next(); listiIter.add(arv); } while (listiIter.hasPrevious()) { int arv = listiIter.previous(); listiIter.set(arv-1); } }
Vaatame listi, mille elementideks on 1, 2, 7 ja 4:
List<Integer> listNumbrid4 = new ArrayList<>(Arrays.asList(1, 2, 7, 4)); meetod4(listNumbrid4);
meetod4
muudab algse listi järgmiseks: [0, 0, 1, 1, 6, 6, 3, 3]
.