E.1 For-løkker
En for-løkke består av de fire delene initialisering, betingelse,
oppdatering og kropp (eng: body). Et typisk eksempel er en for-løkke som
traverserer en tabell a:
for (int i = 0; i < a.length; i++) { // kroppen } Programkode E.1 a)
Her er initialiseringen gitt ved int i = 0, betingelsen er
i < a.length og oppdateringen i++.
Det som står i blokken
innrammet av krøllparentesene { }, er for-løkkens kropp. Den består normalt av en eller
flere programsetninger.
En generell for-løkke ser slik ut:
for (<initialisering>; <betingelse>; <oppdatering>) { // kroppen } Programkode E.1 b)
I flg. eksempel brukes en for-løkke til å travsere en generisk liste ved hjelp av en iterator. La f.eks. listens generiske parametertype være String:
for (Iterator<String> i = liste.iterator(); i.hasNext(); ) { String s = i.next(); // øvrige programsetninger } Programkode E.1 c)
Hvis kroppen har kun én setning, trengs ingen blokk, dvs. innramming av krøllparenteser:
for (int i = 0; i < a.length; i++) <programsetning>;
Det er lurt å ha en blokk selv med kun én setning. Da er blokken klar hvis det senere trengs flere setninger. Ellers er det fort gjort at en ny setning havner utenfor for-løkken. Noen liker å ha alt på én linje hvis det er kun en kort setning. Da droppes blokken.
Variabelen som initialiseres kalles generelt en «tellevariabel» (eng: counter). En står helt fritt til å velge navn på den, men hvis for-løkken arbeider i en tabell, brukes ofte navn som i, j, k, m og n. Det kommer nok av at de samme bokstavene brukes i summeformler i matematikk. Bokstaven l brukes vanligvis ikke siden den i mange tegnsett ligner for mye på sifferet 1.
Det kan være situasjoner der en trenger flere «tellevariabler». Da må det være komma mellom dem og ikke semikolon. I flg. eksempel kopieres innholdet av tabellen a over i tabellen b, men i motsatt rekkefølge:
int[] a = {1,2,3,4,5}; int[] b = new int[a.length]; // like lang som a for (int i = 0, j = b.length - 1; i < a.length; i++, j--) { b[j] = a[i]; // kopierer fra a til b } System.out.println(Arrays.toString(b)); // Utskrift: [5, 4, 3, 2, 1] Programkode E.1 d)
Rekkevidden (eng: scope) til en «tellevariabel» er for-løkkens blokk. I noen situasjoner kan det være gunstig å bryte av (break) en for-løkke når noe spesielt inntreffer. Hvis vi da oppretter tellevariabelen foran for-løkken, vil vi kunne få tak i dens verdi etterpå:
int i = 0; for ( ; i < a.length; i++) { // programsetninger if <et eller annet> break; } // Nå kan vi få tak i verdien til i Programkode E.1 e)
Dette betyr at en eller flere av for-løkkens deler kan være tomme, dvs. ingen kode. Det mest ekstreme er at hverken initialiseringen, betingelsen eller oppdateringen har kode:
for ( ; ; ) { // kropp }
Denne konstruksjonen omtales som en «evig løkke». Det er situasjoner der det er gunstig å sette i gang en løkke på denne måten. Men da må det finnes kode blant programsetningene i kroppen som gjør at løkken garantert brytes på et eller annet tidspunkt.
For-løkker er så vanlige at de fleste utviklingsverktøy har maler (eng: templates)
for dem. Hvis en bruker NetBeans, har en tabell med navn tabell og trenger en
for-løkke som skal arbeide i den, kan en først skrive fori
og så trykke tabulator-tasten. Da kommer dette:
for (int i = 0; i < tabell.length; i++) { int j = tabell[i]; }
I Eclipse kan en skrive for og så trykke CTRL+mellomrom.
Da kommer det tilbud om flere maler. Velges for - iterate over array, kommer dette:
for (int i = 0; i < tabell.length; i++) { }
Netbeans og Eclipse har flere maler enn dette for for-løkker.
E.2 For-alle-løkker
En for-alle-løkke (eng: for-each loop) brukes til å traversere en hel datastruktur.
Det gjelder:
IterableEn generell for-alle-løkke ser slik ut:
for (<datatype> <navn> : <objekt>) { // programsetninger }
Dette kan vi lese slik: for
alle verdier navn av typen datatype fra objekt osv. Her
må objekt være en tabell av den gitte typen eller en instans av en generisk klasse
som er Iterable.
Eksempel med en tabell:
int[] a = {1,2,3,4,5,6,7,8,9,10}; for (int k : a) System.out.print(k + " "); // Utskrift: 1 2 3 4 5 6 7 8 9 10 Programkode E.2 a)
Eksempel med en liste:
List<Character> liste = new LinkedList<>(); liste.add('A'); liste.add('B'); liste.add('C'); for (Character c : liste) System.out.print(c + " "); // Utskrift: A B C Programkode E.2 b)
LinkedList er Iterable siden
LinkedList implementerer List, den
arver (eng: extends) Collection og Collection
arver Iterable. Alle disse tre er grensesnitt (eng: interface).
Når en klasse er Iterable har den garantert metoden iterator(), dvs.
en metode som returnerer en Iterator. Det er den som brukes implisitt i
for-alle løkken.
Java har mange Iterable-klasser.
F.eks. flg. fra java.util:
ArrayDeque, ArrayList, HashSet,
LinkedHashSet, LinkedList,
PriorityQueue, Stack,
TreeSet og Vector.
Eksempel med tabell og mengde (eng: set):
int[] a = {5,9,2,4,7,10,8,1,3,6}; // en tabell Set<Integer> s = new TreeSet<>(); // en mengde (TreeSet) for (int k : a) s.add(k); // fra tabell til mengde for (int k : s) System.out.print(k + " "); // skriver ut // Utskrift: 1 2 3 4 5 6 7 8 9 10 Programkode E.2 c)
E.3 While-løkker
En while-løkke har en betingelse og en kropp (eng: body) og ser
generelt slik ut:
while (<betingelse>) { // kropp }
While-løkken går så lenge som betingelsen er oppfylt. Alt vi kan få til med en for-løkke kan vi få til med en while-løkke, og omvendt. Programkode E.1 a) viser en for-løkke som traverserer en tabell. Dette løses slik ved hjelp av en while-løkke:
int i = 0; while (i < a.length) { // kropp i++; } Programkode E.3 a)
Som nevnt i Avsnitt E.1, kan det av og til være gunstig å sette i gang en «evig løkke». Da må selvfølgelig løkkens «kropp» inneholde kode som gjør at løkken før eller senere brytes (break eller return). En «evig» while-løkke lages slik:
while (true) { // kropp }
Ved fil-lesing brukes ofte en while-løkke. I flg. eksempel finner vi antall linjer på en tekstfil:
FileReader fil = new FileReader("fil.txt"); try (BufferedReader inn = new BufferedReader(fil)) { String s; // skal romme en linje fra filen int antall = 0; while ((s = inn.readLine()) != null) { antall++; } inn.close(); // variabelen antall inneholder nå antall linjer på filen } Programkode E.3 b)
Konstruksjonen over er litt spesiell. Det er linje != null som
er betingelsen, men det utføres en innlesing før den testes. Dette kunne vært løst slik med en for-løkke:
int antall = 0; for (String s = inn.readLine(); s != null; s = inn.readLine()) { antall++; }
E.4 Do-while-løkker
I både for-løkker og while-løkker sjekkes betingelsen før programsetningene i
løkkens kropp utføres. I noen situasjoner kan det være aktuelt å utføre setningene
én gang før betingelsen sjekkes. En do-while-løkke er laget for det formålet.
En slik løkke består av en kropp (eng: body) og en betingelse og ser
generelt slik ut:
do { // kropp } while (<betingelse>);
I Programkode E.3 b) brukte vi en while-løkke til å telle opp antall linjer på en tekstfil. Dette kan også løses ved en do-while-løkke. Legg merke til at variabelen antall nå blir initiert til -1. Det kommer av at setningen antall++ blir utført én gang selv om filen ikke skulle ha noen linjer, dvs. den er tom.
FileReader fil = new FileReader("fil.txt"); try (BufferedReader inn = new BufferedReader(fil)) { String s; // skal romme en linje fra filen int antall = -1; do { s = inn.readLine(); antall++; } while (s != null); inn.close(); // variabelen antall inneholder nå antall linjer på filen } Programkode E.4 a)
|