char
String
-objekterfor
-løkkeEn array (éndimensjonal tabell) består av et bestemt antall dataelementer av samme type. Elementene er indeksert fra 0 og oppover.
typenavn[] arraynavn;
eller
typenavn arraynavn[];
Legg merke til at vi i deklarasjonen ikke sier noe om hvor mange elementer det skal være plass til. (Det gjør vi først når vi oppretter arrayen, se nedenfor.) Typenavnet kan være en hvilken som helst datatype. Navnet på arrayen velger vi selvsagt (som alle navn) slik at det er mest mulig selvforklarende, det vil si indikerer hvilken rolle arrayen skal spille i programmet. Det er likegyldig for programmets virkemåte hvilket av disse to alternativene vi bruker for å deklarere en array. Men den første av dem er mest i samsvar med deklarasjon av variable av andre datatyper. Det er den som vil bli brukt i denne teksten og tilhørende programeksempler.
int[] frekvenser; double[] priser; String[] forfattere;
En array vil ikke eksistere (det vil si vi kan ikke bruke den) før den er
opprettet. For å opprette en array bruker vi
new
-operatoren ved å skrive en instruksjon på
formen
arraynavn = new typenavn[ antallElementer ];
der arraynavn
og
typenavn
må stemme overens med det vi har
skrevet i deklarasjonen og antallElementer
angir hvor mange elementer (av den angitte type) arrayen skal ha plass til.
For å opprette arrayer, bruker vi altså new
-operatoren, slik
vi også har gjort for å opprette objekter definert av klasser. Grunnen til
dette er at i java så er også arrayer en type objekter. Arraynavnet
(arrayvariabelen) vil derfor være en referanse (peker) til array-objektet, vi kan
tenke på den som arrayens memory-adresse.
Med de arraydeklarasjonene som står ovenfor kan vi opprette følgende arrayer:
frekvenser = new int[ 20 ]; priser = new double[ 100 ]; int antForfattere = 1000; forfattere = new String[ antForfattere ];
Antall elementer må være et heltall eller et heltallsuttrykk, for eksempel
en heltallsvariabel som på forhånd har fått verdi, som i siste eksemplet
ovenfor. I mange tilfelle vil det være aktuelt å foreta innlesing av antall
elementer. NB! Indekseringen for en array går alltid fra 0
til antall elementer minus 1. For eksempel vil den for arrayen
frekvenser
gå fra 0 til 19. Arrayer blir
automatisk initialisert samtidig som de blir opprettet. Numeriske
arrayer (altså arrayer av numerisk datatype) blir initialisert til
tallverdien 0 (eller 0.0, avhengig av datatype) på alle plasser. Arrayer av
datatype boolean
initialiseres automatisk til verdien
false
på alle plasser, mens alle arrayer av objekter, det vil si av datatype
definert av en klasse eller av en array, blir initialisert til verdien
null
på alle plasser. For å
referere til enkeltelementer i en array bruker vi arraynavn og indeks.
Arraynavnet kalles derfor også for en indeksert variabel.
Med arrayene som er opprettet ovenfor, kan vi for eksempel skrive instruksjonene
frekvenser[ 0 ] = 17;
frekvenser[ 1 ] = 3;
priser[ 5 ] = 213.50;
forfattere[ 17 ] = "Lars Saabye Christensen";
Arrayer kjenner sin egen lengde. Vi får tak i den ved å skrive
arraynavn.length
. Dette er til nytte blant
annet når arrayer skal gjennomløpes for prosessering av elementene. Det er
da vanlig å bruke en for
-løkke, der
styrevariabelen samtidig tjener som arrayindeks.
for ( int i = 0; i < frekvenser.length; i++ ) { < gjør et eller annet med frekvenser[ i ] > } for ( int i = 0; i < forfattere.length; i++ ) { < gjør et eller annet med forfattere[ i ] > }
Det er tillatt å opprette en array samtidig som den blir deklarert.
For eksempel kunne vi for arrayen
frekvenser
, som er deklarert og opprettet
ovenfor, isteden ha skrevet
int[] frekvenser = new int[ 20 ];
Det er mulig, samtidig som en deklarerer en array, å initialisere den ved hjelp av en liste av verdier omsluttet av krøllparenteser. For utlånsobjekter på et mediatek kunne vi for eksempel ha opprettet en array på denne måten:
String[] utlånsobjekter = { "bok", "CD", "DVD", "spill" };
Merk deg at når det gjøres på denne måten, er det ikke tillatt å splitte opp i to instruksjoner. Vi kunne altså ikke ha skrevet
String[] utlånsobjekter; utlånsobjekter = { "bok", "CD", "DVD", "spill" };
Merk deg også at vi ikke spesifiserer noen størrelse på
arrayen når den blir opprettet ved hjelp av en initialiseringsliste som
vist ovenfor. Riktig størrelse vil bli bestemt automatisk. Som ellers
kan vi få tak i den ved (i dette tilfelle) å skrive
utlånsobjekter.length
. Indekseringen for
en slik array vil, som for alle andre, gå fra 0 og oppover.
Vi skal lage et program som gir brukeren mulighet til gjentatte ganger å trekke en tilfeldig av årets tolv måneder. Brukeren skal hver gang få spørsmål om han/hun ønsker å trekke en ny måned. I tilfelle det svares "Ja", skal ny måned trekkes og trukket måned skal skrives ut i en meldingsboks. Svares det "Nei", skal programmet avsluttes. Fullstendig programkode for programmet er gjengitt lengre nede. Først skal vi se på noen problemer som må løses under programmeringen.
Det første problemet som vi skal ta for oss, er hvordan vi kan foreta
trekning blant månedene. Siden dette med å trekke en måned er noe som det også
kan bli behov for i andre sammenhenger, velger vi å definere en egen
klasse som inneholder en metode for nettopp dette. Hver gang vi har behov for
å trekke en tilfeldig måned, kan vi dermed opprette et objekt av denne klassen
og bruke nevnte metode. Klassen har vi nedenfor kalt Trekningsautomat
.
Siden det er 12 måneder, kan vi trekke måned ved å trekke et av tallene 1 til 12 og skrive ut det månedsnavnet som dette tallet svarer til. Men hvordan kan vi programmere denne tilordningen mellom tall og månedsnavn? Det kan vi få til ved å legge månedsnavnene inn i en array, for der vil hvert navn svare til en arrayindeks. Nå må vi imidlertid huske på at arrayindeksene alltid går fra 0 og oppover. Istedenfor å trekke et tall fra 1 til 12 kan vi derfor trekke et tall fra 0 til 11 og velge den måneden som har det trukne tallet som indeks i arrayen
String[] månedsnavn = { "Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember" };
I klassen Trekningsautomat
, som er gjengitt nedenfor, er
arrayen med månedsnavn lagt inn på klassenivå. Der er det også lagt inn en
slumptallsgenerator. Metoden som trekker og returnerer månedsnavn kan vi
dermed skrive på følgende måte:
public String trekkMåned() { int indeks = generator.nextInt( månedsnavn.length ); return månedsnavn[ indeks ]; }
Et objekt av type Trekningsautomat
skal vi nå bruke i et
program som virker på den måte som ble beskrevet ovenfor. Vi skal først se
nærmere på hvordan vi kan få programmet til å vise dialogbokser med Ja- og Nei-knapper.
Ja-nei-dialogbokser ble introdusert i kapittel 3, og vi har brukt dem i flere programeksempler. Men på knappene i dialogboksene har det stått "Yes" og "No" istedenfor "Ja" og "Nei". Nå, som vi har lært om arrayer, har vi grunnlag for å lære hvordan vi kan få "Ja" og "Nei" på knappene, istedenfor "Yes" og "No". En slik dialogboks kan vi få ved å skrive følgende kode:
String[] alternativer = { "Ja", "Nei" }; JOptionPane.showOptionDialog( null, "Vil du trekke en måned?", "Månedstrekning", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, alternativer, alternativer[ 0 ] )
Arrayen alternativer
inneholder tekstene som vi ønsker å ha
på knappene i dialogboksen vår.
Parametrene i kallet på metoden showOptionDialog
har følgende
betydning:
null
null
."Vil du trekke en måned?"
"Månedstrekning"
JOptionPane.YES_NO_OPTION
JOptionPane.QUESTION_MESSAGE
null
alternativer
alternativer[ 0 ]
På et av bildene nedenfor kan du se boksen som denne koden resulterer i.
På bildet kan du også tydelig se at "Ja"-knappen er forhåndsvalgt, ved at
"Ja" på vedkommende knapp er rammet inn av en ekstra firkant. En slik finnes
ikke rundt "Nei" på den andre knappen. For øvrig må denne boksen brukes på
samme måte som vi tidligere har brukt bokser med "Yes" og "No" på knappene.
Vi må blant annet fange opp svaralternativet som brukeren har valgt og finne
ut hva det er, for å kunne velge riktig alternativ for programmets videre kjøring.
Fullstendig kode for dette finnes i klassen Maanedstrekning
som er
gjengitt nedenfor.
1 import java.util.Random; 2 3 public class Trekningsautomat 4 { 5 private String[] månedsnavn = { "Januar", "Februar", "Mars", "April", 6 "Mai", "Juni", "Juli", "August", "September", 7 "Oktober", "November", "Desember" }; 8 private Random generator = new Random(); 9 10 public String trekkMåned() 11 { 12 int indeks = generator.nextInt( månedsnavn.length ); 13 return månedsnavn[ indeks ]; 14 } 15 }
1 import javax.swing.JOptionPane; 2 3 public class Maanedstrekning 4 { 5 public static void main( String[] args ) 6 { 7 Trekningsautomat trekker = new Trekningsautomat(); 8 boolean flere = true; 9 String[] alternativer = { "Ja", "Nei" }; 10 11 do 12 { 13 int svar = JOptionPane.showOptionDialog( null, 14 "Vil du trekke en måned?", "Månedstrekning", 15 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, 16 null, alternativer, alternativer[ 0 ] ); 17 flere = (svar == JOptionPane.YES_OPTION ); 18 if ( flere ) 19 { 20 String måned = trekker.trekkMåned(); 21 JOptionPane.showMessageDialog( null, "Trukket måned: " + måned, 22 "Månedstrekning", JOptionPane.PLAIN_MESSAGE ); 23 } 24 } while ( flere ); 25 } 26 }
Følgende bilder viser Ja-nei-dialogboksen som vises av programmet og
en dialogboks med en trukket måned, som følge av at brukeren har svart "Ja"
på spørsmålet om det ønskes trekning av måned. Legg merke til at dersom
brukeren klikker på Nei-knappen, så vil variabelen flere
få verdi
false
. Det vil da ikke bli foretatt noe trekning av måned og
programmet vil bli avsluttet.
Du kan få lastet ned programfilene ved å klikke på følgende linker:
Trekningsautomat.java
,
Maanedstrekning.java
.
Lag et program som leser inn 10 heltall fra brukeren og skriver dem ut igjen i motsatt rekkefølge med ett tall per linje i en dialogboks. (Lagre de innleste tallene i en array.)
Som i Oppgave 1 skal du lage et program som foretar 10 innlesinger av heltall fra brukeren. Det er imidlertid bare tall som er i intervallet 1 til 50 (begge grenser inkludert) som skal lagres. Men også innlesing av tall utenfor dette intervall teller med blant de 10 innlesingene. Når innlesingene er fullført, skal programmet skrive ut (i en dialogboks) 10 linjer med stjerner. Antall stjerner på hver linje skal være lik de tallene som ble lest inn fra brukeren. På linjer som svarer til innlesinger der det ble lest inn tall utenfor intervallet 1 til 50, skal det ikke skrives noen stjerner (altså bare en blank linje).
Klassen Frekvenser
som er gjengitt nedenfor finnes i fila
Frekvenser.java.
Klassen definerer et objekt som skal kunne trekke et antall slumptall og
registrere frekvensfordelingen for dem. De trukne tallene kan være fra 1
og opptil en gitt maksimalverdi. Konstruktøren mottar via parametre antall
tall som skal trekkes og maksimalverdi for dem. Den lagrer de to parameterverdiene i datafeltene
antall
og maks
.
Arrayen frekvenser
skal brukes til å
registrere frekvensene for slumptallene som trekkes. Denne blir opprettet av
konstruktøren med antall plasser lik antall mulige utfall for slumptallene som
skal trekkes. Arrayen vil ved opprettelsen automatisk bli
initialisert til 0 på alle plasser. Arrayindeksene vil gå fra 0 til maks - 1
.
Arrayen skal brukes på den måten at frekvenser[0]
skal fortelle hvor mange ganger tallet 1 ble trukket,
frekvenser[1]
skal fortelle hvor mange ganger
tallet 2 ble trukket, og så videre. Det betyr generelt at dersom tallet
n
blir trukket, så er det verdien som ligger i
frekvenser[n - 1]
som skal økes med 1 for å
registrere at nå er n
blitt trukket en gang til. Det
oppnår vi ved å skrive frekvenser[n - 1]++
.
Trekning av slumptall og registrering av frekvenser for dem utføres av
metoden trekk
. (Metoden må selvsagt kalles opp for at det skal
bli utført!) Slumptallsgeneratoren generator
, som ble opprettet
av konstruktøren, brukes til å foreta trekningen.
Metoden skrivUt
har som oppgave å skrive ut resultatet av
frekvensfordelingen.
Når innholdet av arrayen skal skrives ut, blir den på vanlig måte
gjennomløpt ved bruk av en for
-løkke.
De enkelte verdiene blir skrevet ut ved et kall på utskriftsområdets
append
-metode. Vi får lagt inn passe avstand
mellom hvert (tall: frekvens)-par ved å tilføye et tabulatortegn
"\t"
.
Vi ønsker dessuten å fordele verdiene slik at det blir inntil 10 (tall: frekvens)-par
på hver linje. Dersom styrevariabelen i
for
for
-løkka er delelig med 10, betyr det at vi allerede
har skrevet ut 10 (tall: frekvens)-par på vedkommende linje. Vi går da til ny linje ved å tilføye et linjeskifttegn.
Et "driverprogram" for klassen Frekvenser
finnes i fila
Frekvenstest.java.
Denne inneholder en main
-metode som oppretter et
Frekvenser
-objekt og gjør kall på objektets metoder
trekk
og skrivUt
. Ved opprettelsen av objektet er
det brukt konstruktørparametrene 2000 og 20. Utskriften fra programmet ble da
som vist på bildet nedenfor. Siden det er slumptall som trekkes, vil frekvensfordelingen
variere fra kjøring til kjøring av programmet. Men siden det er 20 forskjellige
tall det trekkes blant og trekning foretas 2000 ganger, burde vi hver gang kunne vente
oss at hvert tall trekkes omkring 100 ganger. På utskriften kan vi se hvor vidt
dette stemmer.
1 import javax.swing.JOptionPane; 2 import javax.swing.JTextArea; 3 import java.util.Random; 4 5 public class Frekvenser 6 { 7 private int antall; //antall tall som skal trekkes 8 private int maks; //skal trekke tall i intervallet 9 //fra og med 1 til og med maks 10 private int[] frekvenser; //skal inneholde frekvensene 11 //for tallene som trekkes 12 private Random generator; 13 14 public Frekvenser( int a, int m ) 15 { 16 antall = a; 17 maks = m; 18 frekvenser = new int[ maks ]; 19 generator = new Random(); 20 } 21 22 //trekker slumptall og registrerer frekvenser 23 public void trekk() 24 { 25 for ( int i = 0; i < antall; i++ ) 26 { 27 int trukket = 1 + generator.nextInt( maks ); 28 frekvenser[ trukket - 1 ]++; //husk at indeksering starter på 0 29 } 30 } 31 32 //Lager utskrift i et tekstområde: 33 public void skrivUt() 34 { 35 JTextArea utskrift = new JTextArea(); 36 utskrift.setText( "Frekvenser for trekning av " + antall + 37 " slumptall fra intervallet 1 til " + maks + "\n" ); 38 //går gjennom frekvenstabell og skriver ut verdiene 39 for ( int i = 0; i < frekvenser.length; i++ ) 40 { 41 if ( i % 10 == 0 ) //går til begynnelsen av neste linje 42 { 43 utskrift.append( "\n" ); 44 } 45 utskrift.append( (i + 1) + ": " + frekvenser[ i ] + "\t" ); 46 } 47 48 JOptionPane.showMessageDialog( null, utskrift, "Frekvenser", 49 JOptionPane.PLAIN_MESSAGE ); 50 } 51 }
I kapittel 6, eksempel 2 er det et program der det blir simulert kast med en terning 6000 ganger. Programmet registrerer og skriver ut hvor mange ganger det blir seks på terningen. Du får nå som oppgave å omarbeide programmet slik at det registrerer og skriver ut frekvensene for alle de seks mulige terningverdiene. Bruk en array til registrering av frekvensene.
Lag et program som leser inn hele tall og lagrer dem i en
array. Programmet skal imidlertid bare lagre de tall som ikke er lest inn
allerede. (Arrayen skal altså ikke inneholde noen duplikater av tall.)
Alle
tall som leses inn skal være i intervallet 20 til 100, begge grenser inkludert.
Programmet skal lese inn i alt 20 forskjellige tall fra dette intervallet.
Programmet skal skrive
ut feilmelding hver gang brukeren prøver seg med en verdi
utenfor det nevnte intervall, og programmet skal foreta ny innlesing av dette
tallet gjentatte ganger inntil det leses inn et tall fra det riktige intervallet.
Når alle tallene er lest inn, skal programmet skrive
ut i et tekstområde de forskjellige tallene som er lest inn. Legg inn
mellomrom
mellom tallene og skriv ut høgst 5 tall per linje.
(I programeksempel 2 ovenfor, der det skrives ut 10 tall per linje,
kan du se hvordan du kan få
til utskriften på denne måten.)
Programmet kan lages ved at du skriver en klasse
etter følgende skisse (nødvendige
import
-instruksjoner må tas med i tillegg):
public class Tallduplikater { <Deklarasjon av array for lagring av innleste tall og andre nødvendige variable og konstanter.> public Tallduplikater() { < Foretar nødvendig initialisering av variable > } public void lesInnTall() { < Leser inn 20 tall fra riktig intervall. Foretar eventuell ny innlesing for tall som ikke er i riktig intervall. For hvert innlest tall (fra riktig intervall) sjekkes det om samme tall er lest inn tidligere. Dersom det ikke er tilfelle, lagres tallet i arrayen for dette. > } public boolean finnes( int tall ) { < Returnerer true eller false avhengig av om tall finnes i arrayen med innleste tall. > } public void visInnlesteTall() { < Skriver innhold av arrayen med innleste tall ut i tekstområde, høgst 5 tall per linje. Viser tekstområdet i en meldingsboks.> } }
I tillegg må du lage et "driverprogram" som oppretter et objekt av den definerte typen og foretar de nødvendige metodekallene.
char
Den primitive datatypen char
brukes til å
representere enkelttegn. Verdiene vil være de unicode-verdiene som
identifiserer tegnene. En tegn-literal omsluttes av apostrofer.
Vi kan for eksempel skrive
char tegn = 'a';
Merk deg forskjellen mellom 'a'
og
"a"
! Den første er et tegn (mer presist:
en verdi av datatypen char
), mens den andre
er en tekststreng (det vil si en verdi av datatypen
String
). Vi kan, selvsagt, også opprette
arrayer av tegn, slik som
char[] vokaler = { 'a', 'e', 'i', 'o', 'u', 'y', 'æ', 'ø', 'å' };
Men merk deg at en array av tegn ikke er det samme som en
String
! (På den andre side kan vi på grunnlag
av en array av tegn opprette en String
med
tilsvarende innhold. Dette er noe vi kommer tilbake til når vi skal se nærmere
på String
-datatypen.)
String
-objekterTekststrenger (det vil si String
-objekter)
er på samme måte som arrayer indeksert fra 0 og oppover.
String
-objekter har også det til felles med
arrayer at de kjenner sin egen lengde. Lengden returneres av
String
-metoden
length()
.
String eks = "eksempel"; int stringlengde = eks.length(); // stringlengde == 8 char[] stringtegn = { 'e', 'k', 's', 'e', 'm', 'p', 'e', 'l' }; int tabellengde = stringtegn.length; // Merk forskjellen! // tabellengde == 8
For å få tak i tegnet som ligger i en gitt indeksposisjon i en streng,
bruker vi String
-metoden
charAt
.
String eks = "eksempel"; char tegn = eks.charAt( 2 ); // tegn == 's'
Programmet Vokaler.java leser inn en tekststreng fra brukeren. Programmet skriver ut alle vokaler som forekommer i den innleste tekststrengen og det totale antallet vokaler. Skjermbildene viser eksempel på en kjøring av programmet.
Kildekoden er gjengitt nedenfor.
1 import javax.swing.JOptionPane; 2 3 public class Vokaler 4 { 5 public static void main( String[] args ) 6 { 7 String input = JOptionPane.showInputDialog( 8 "Programmet teller og skriver " + 9 "ut vokalene i tekststrengen du skriver." ); 10 11 char[] vokaler = { 'a', 'e', 'i', 'o', 'u', 'y', 'æ', 'ø', 'å' }; 12 int antall = 0; 13 String output = "Vokaler i innlest tekst:\n"; 14 15 for ( int i = 0; i < input.length(); i++ ) 16 { 17 char tegn = input.charAt( i ); 18 19 for ( int j = 0; j < vokaler.length; j++ ) 20 { 21 if ( tegn == vokaler[ j ] ) 22 { 23 antall++; 24 output += tegn + " "; 25 //vil bare ha 5 vokaler på hver linje i utskriften 26 if ( antall % 5 == 0 ) 27 output += "\n"; 28 } 29 } 30 } 31 32 output += "\nI alt " + antall + " vokaler."; 33 JOptionPane.showMessageDialog( null, output, "Vokaler", 34 JOptionPane.PLAIN_MESSAGE ); 35 } 36 }
Lag et program som leser inn en tekststreng fra brukeren. Programmet skal telle opp og skrive ut antall forekomster det i den innleste tekststrengen er av de forskjellige bokstavene i alfabetet, altså antall a-er, antall b-er, etc. Det skal skilles mellom små og store bokstaver. (Programmet skal altså telle opp både antall a-er og A-er, og tilsvarende for de andre bokstavene.)
Det er viktig å ha klart for seg at arrayer er objekter og at en array-variabel bare er en peker (referanse) til array-objektet. Dette har betydning når vi foretar tilordning mellom array-variable og når array-variable er metodeparametre. Vi kan belyse dette gjennom et lite eksempel.
int[] x = { 1, 2, 3 }; int[] y = { 4, 5, 6 };
Nå peker x
og y
på hvert sitt array-objekt.
Vi foretar så følgende instruksjon:
y = x;
Resultatet blir at y
nå vil peke på samme
array-objekt som x
peker på, x
og y
vil
ikke lenger peke på hvert sitt array-objekt. Array-objektet som y
pekte på tidligere vil vi miste fordi vi ikke lenger har noen peker til det.
(Det vil automatisk bli slettet av java-systemet.)
Dersom vi ikke er klar over at array-variable fungerer som pekere til
objekter, kan vi feilaktig tro at instruksjonen y = x;
ovenfor
har som resultat at innholdet i arrayen som x
representerer vil
bli kopiert over til arrayen som y
representerer. Dersom det er
dette vi ønsker, og altså dessuten at x
og y
fortsatt
skal representere hver sin array, kan vi skrive følgende kode:
if ( x.length <= y.length ) { for ( int i = 0; i < x.length; i++ ) y[ i ] = x[ i ]; }
Krøllparentesene er her strengt tatt unødvendige, men de bidrar til å
klargjøre strukturen. Det finnes for øvrig i javas klassebibliotek (i pakken
java.lang
) det vi kan kalle en verktøymetode med navn
arraycopy
som kan brukes til
å kopiere innholdet i en array (eller en del av denne) over i en annen. I vårt tilfelle (med
x
og y
slik de opprinnelig ble opprettet ovenfor)
kunne vi ved bruk av denne verktøymetoden ha skrevet
System.arraycopy( x, 0, y, 0, x.length );
Parametrene i metoden arraycopy
har følgende betydning:
1. parameter er arrayen som det skal kopieres fra, kilden;
2. parameter er indeksen for startposisjonen (for kopieringen) i kilden;
3. parameter er arrayen det skal kopieres til, målet;
4. parameter er indeksen for startposisjonen (for kopieringen) i målet;
5. parameter er antall elementer som skal kopieres.
Det er en svært viktig forskjell i virkemåte mellom parametre av primitiv datatype og parametre av arraytype eller klassetype (det vil si pekere). Definisjon av en metode med én parameter ser generelt slik ut (uttrykt i pseudokode):
returdatatype metodenavn( datatype parameter )
{
< instruksjoner som skal utføres av metoden,
bruker parameter som en lokal variabel >
}
Ved kall på metoden må det brukes en aktuell parameter (også kalt argument) av riktig datatype og med tildelt verdi:
datatype variabel = ...;
//metodekall:
returdatatype r = metodenavn( variabel );
Virkemåten for metodens aktuelle parameter variabel
kan vi
beskrive slik:
datatype
er primitiv (int, double,
etc.): Metoden arbeider med en kopi av
variabel
og vil ikke endre dens verdi. (Verdien til
variabel
er startverdi for den lokale variabelen
parameter
i metoden.)datatype
er arraytype eller klassetype:
Metoden arbeider med det objekt som
variabel
refererer til (peker på) og kan endre dets
innhold. (Metoden vil arbeide med en kopi av
adressen som variabel
inneholder og vil derfor
operere på samme objekt som variabel
peker på.
Metoden kan ikke få variabel
til å endre
verdi, det vil si til å peke på et objekt med annet innhold.)Programmet som består av de to klassene
Arrayparametre
og Arraybehandler
viser et eksempel på hvordan en arraytype virker
både som parameter og som retur-datatype for metoder.
Programmet er organisert ved at klassen Arraybehandler
inneholder
noen metoder som utfører diverse operasjoner på arrayer, mens klassen
Arrayparametre
er et testprogram for de definerte metodene.
Klassen Arraybehandler
inneholder metoder for å opprette og returnere en slumptallsarray av ønsket størrelse,
reversere mottatt array, tilføye utskrift av mottatt array i tekstområdet metoden mottar,
samt en metode som dobler og returnerer mottatt heltallsparameter.
Klassen er gjengitt nedenfor. Vi skal se litt nærmere på de enkelte metodene
i klassen.
Metoden lagListe
oppretter og returnerer en array fylt av
slumptall. Legg merke til at arrayen opprettes inne i metoden.
Metoden returnerer array-variabelen. Denne vet vi er en referanse
til arrayobjektet. Det vil derfor være den som blir returnert av metoden.
Metoden reverser
har en array som parameter. Metoden reverserer
rekkefølgen til arrayelementene. Metoden mottar altså (via parameteren)
en referanse til arrayobjektet og endrer dette objektet.
Den returnerer ikke noe.
Metoden print
har som oppgave å tilføye på et tekstområde
en utskrift av innholdet i en array. Metoden mottar via parametre både
arrayen den skal skrive ut og det tekstområdet den skal skrive det ut på.
Dessuten mottar den en overskrift som skal stå over arrayinnholdet.
Dermed kan metoden skrive ut innhold av forskjellige arrayer og med forskjellig
overskrift for hver gang den blir kalt opp, og om ønskelig, på forskjellige
tekstområder. Legg merke til at den jobber med de objektene den mottar, den
returnerer ikke noe.
Metoden dubler
har en int
-parameter som den
dobler og returnerer. Siste arrayelement brukes som aktuell parameter når
det gjøres kall på metoden i klassen Arrayparametre
.
Utskrift av dette siste arrayelementet før og etter
metodekallet viser at metoden ikke endrer verdien til arrayelementet. Husk at metodeparametre
fungerer som lokale variable i metoden. Aktuelle parametre gir startverdi til
dem. I vårt tilfelle gir siste arrayelement startverdi til den lokale variabelen
k
. Det er verdien til denne lokale variabelen metoden dobler og som den
returnerer den doblete verdien til.
Verdien til siste arrayelement er uberørt av dette.
1 import java.util.Random; 2 import javax.swing.JTextArea; 3 4 public class Arraybehandler 5 { 6 //returnerer slumptallsarray med lengde lik første parameter og 7 //slumptall fra og med 0 til andre parameter 8 public int[] lagListe( int lengde, int grense ) 9 { 10 int[] slumptall = new int[ lengde ]; 11 Random generator = new Random(); 12 for ( int i = 0; i < slumptall.length; i++ ) 13 slumptall[ i ] = generator.nextInt( grense ); 14 return slumptall; 15 } 16 17 //reverserer arrayen som er parameter 18 public void reverser( int[] tabell ) 19 { 20 int n = tabell.length - 1; 21 for ( int i = 0; i < tabell.length / 2; i++ ) 22 { 23 int elem = tabell[ i ]; 24 tabell[ i ] = tabell[ n - i ]; 25 tabell[ n - i ] = elem; 26 } 27 } 28 29 //printer ut array med ønsket overskrift 30 public void print(JTextArea utskrift, String overskrift, int[] liste) 31 { 32 utskrift.append( overskrift + "\n" ); 33 for ( int i = 0; i < liste.length; i++ ) 34 { 35 utskrift.append( String.valueOf( liste[ i ] ) + "\t" ); 36 if ( (i + 1) % 10 == 0 ) 37 utskrift.append( "\n" ); 38 } 39 } 40 41 // dobler parameterverdien og returnerer den 42 public int dubler( int k ) 43 { 44 k *= 2; 45 return k; 46 } 47 }
Klassen Arrayparametre
som er gjengitt nedenfor inneholder
bare en main
-metode som fungerer som testprogram for
Arraybehandler
-klassen. Det gjør den på den måten at den oppretter
et Arraybehandler
-objekt og bruker dette objektet til å gjøre
kall på de forskjellige metodene i Arraybehandler
-klassen.
Den lager dessuten utskrifter som viser virkningen av de forskjellige metodene
som blir kalt opp. Merk deg hvordan metodene blir kalt opp og hvordan
deres returverdier blir fanget opp og brukt videre i programutførelsen.
Legg merke til at print
-metoden blir kalt opp to ganger,
først for å skrive ut den opprinnelige arrayen, og deretter for å skrive
ut den reverserte arrayen. De to utskriftene blir forsynt med hver sin
overskrift.
Legg for øvrig merke til at det er de forskjellige metodene som blir kalt opp fra
Arraybehandler
-klassen som gjør det aller meste av jobben
under programutførelsen!
Instruksjonen på linje 15 skalerer størrelsen på tabulatorhoppene som
escapetegnet "\t"
gir. Hensikten er å få passe avstand mellom
arrayverdiene i utskriften.
1 //Illustrasjon av arrayparametres virkemåte sammenliknet med 2 //parametre av primitiv type 3 import javax.swing.JTextArea; 4 import javax.swing.JOptionPane; 5 6 public class Arrayparametre 7 { 8 public static void main( String[] args ) 9 { 10 Arraybehandler behandler = new Arraybehandler(); 11 int tallgrense = 20; 12 //oppretter slumptallsliste og printer den ut 13 int[] liste = behandler.lagListe( 10, tallgrense ); 14 JTextArea utskrift = new JTextArea(); 15 utskrift.setTabSize(5); 16 utskrift.setText( "Illustrasjon av arrayparametres virkemåte " + 17 "sammenliknet med parametre av primitiv type\n" ); 18 behandler.print( utskrift, "Opprinnelig array", liste ); 19 20 //reverserer lista og printer ut reversert liste 21 behandler.reverser( liste ); 22 utskrift.append( 23 "\nReverserer arrayen ved å bruke den som parameter " + 24 "i en metode som utfører reverseringen." ); 25 behandler.print( utskrift, "\nReversert array", liste ); 26 27 //printer ut siste listeelement 28 utskrift.append( 29 "\nSiste arrayelement " + liste[ liste.length - 1 ] + 30 " skal brukes som aktuell parameter en metode som dobler " + 31 "mottatt parameterverdi." ); 32 33 //viser virkning av dubleringsmetode 34 int doblet = behandler.dubler( liste[ liste.length - 1 ] ); 35 utskrift.append( "\nDoblet verdi av aktuell parameter: " + 36 doblet ); 37 utskrift.append( "\nAktuell parameter har beholdt sin verdi: " + 38 liste[ liste.length - 1 ] ); 39 40 JOptionPane.showMessageDialog( null, utskrift, "Arrayparametre", 41 JOptionPane.PLAIN_MESSAGE ); 42 } 43 }
Bildet nedenfor viser resultatet av en kjøring av programmet. Siden det bruker slumptall, vil det bli forskjellig resultat fra kjøring til kjøring.
I java er det mulig å opprette arrayer uten å ha noen referanse til dem, slik at vi får det vi kan kalle en anonym array. Her er eksempel på det:
new int[] { 2, 3, 5, 7, 11, 13 };
Virkningen er at det blir opprettet et array-objekt med det innholdet som
er listet opp mellom krøllparentesene. Datatypen må selvsagt stemme med det
som er angitt foran, i dette tilfelle int
.
Størrelsen på arrayen blir lik det antall elementer som er listet opp.
Det er neppe ofte vi får bruk for å gjøre dette, men det er greit å kjenne til det,
slik at vi vet hva det betyr i tilfelle vi treffer på det når vi leser andres
programkode. For øvrig kan det vel tenkes at det kan være aktuelt å bruke en anonym array
for eksempel som aktuell parameter i enkelte metodekall. I main
-metoden
til klassen Arrayparametre
ovenfor hadde det for eksempel vært tillatt
med følgende metodekall:
behandler.print(utskrift, "De første primtall", new int[] { 2, 3, 5, 7, 11, 13 });
Det er også mulig å bruke denne teknikken for å reinitialisere en array direkte, uten å måtte opprette en ny variabel, slik som i følgende eksempel:
String[] arbeidsdager = {"mandag", "tirsdag", "onsdag", "torsdag", "fredag"}; ... arbeidsdager = new String[] {"man", "tir", "ons", "tor", "fre"};
Koden i siste linje ovenfor er en forkortelse for følgende to kodelinjer:
String[] anonym = new String[] {"man", "tir", "ons", "tor", "fre"}; arbeidsdager = anonym;
for
-løkkeVi har sett at det ofte er behov for å bruke for
-løkker
for gjennomløping av hele eller deler av arrayer. Fra og med javaversjon 1.5 er det
innført en forenklet skrivemåte som noen ganger kan brukes for
for
-løkker som skal gjennomløpe hele arrayen. Som
eksempel på denne tar vi for oss følgende metode som skal beregne og
returnere summen av alle elementene i en array av hele tall. (Følgende
versjon av metoden er skrevet på tradisjonell måte.)
public int sum( int[] liste ) { int total = 0; for ( int i = 0; i < liste.length; i++ ) total += liste[ i ]; return total; }
Uttrykt med den nye versjonen av for
-løkke kan vi skrive
samme metoden på følgende måte (men den forutsetter bruk av javaversjon 1.5
eller seinere versjon):
public int sum( int[] liste ) { int total = 0; for ( int element : liste ) total += element; return total; }
Vi ser at den nye versjonen av for
-løkke er litt mer kompakt, men den virker,
iallfall ved første øyekast, også mer kryptisk. Vi kan lese den noe sånn som
"for alle hele tall element
i arrayen liste
...". Løkka vil bli utført
for alle elementene i vedkommende array. For hvert løkkegjennomløp vil variabelen
element
bli satt lik vedkommende arrayelement. Datatypen for
variabelen må derfor stemme overens med datatypen for elementene i vedkommende array. Navnet til
variabelen (element
i dette tilfelle) kan velges fritt, slik som
for variable ellers. En slik "utvidet for
-løkke" har imidlertid
noen begrensninger:
På grunn av de nevnte begrensninger er det ikke så ofte vi kan bruke en løkke av denne type, og vi trenger selvsagt ikke å gjøre det i det hele tatt. Det er ingen ting i veien for å fortsette med den tradisjonelle løkketypen også i de tilfeller der det er mulig å bruke den nye.
Parallelt med introduksjonen av den utvidete for
-løkka, ble det
i javaspråket innført en mulighet for at en metode kan ha et varierende antall
argumenter av samme datatype. Syntaksen for dette er som antydet her:
returverditype metodenavn(datatype ... parameternavn)
{
< metodeinnhold >
}
Det som signaliserer at det aktuelle parameterantallet kan være varierende,
er altså de tre prikkene ...
mellom datatype og parameternavn.
Ved kall på metoden kan vi bruke så mange paramtre vi vil av den spesifiserte typen,
eventuelt ingen. Vi lister da opp parametrene med komma mellom hver parameter.
I tillegg til den uspesifiserte lista av parametre kan metoden ha andre parametre,
så mange vi vil. Men det er et krav at lista med varierende antall parametre må stå
til slutt, og det kan bare være én slik liste.
Reint teknisk virker dette på den måten at parametrene som kan variere i antall
blir lagt inn i en array når metoden blir utført. Navnet på vedkommende parameter
er derfor i virkeligheten en arrayvariabel av den datatype som er spesifisert.
Inni metoden kan denne arrayen gjennomløpes for prosessering. Til dette kan vi
bruke en for
-løkke. Siden det
i dette tilfelle vil være hele arrayen som skal gjennomløpes, kan vi bruke en
utvidet for
-løkke. Selvsagt
kunne vi også brukt en vanlig for
-løkke.
Det er sannsynligvis ikke så ofte du vil ha bruk for å programmere med denne type parameterliste, men igjen så er det nyttig å kjenne til syntaksen, slik at du kan forstå betydningen i tilfelle du treffer på den i kode som andre har skrevet.
For å lage et eksempel på definisjon av metode med varierende antall parametre,
skal vi gå tilbake til eksempel 3 i kapittel 6.
Det ble der laget tre forskjellige versjoner av metoden antSekunder
for konvertering til sekunder av et tidsspenn som kunne angis enten i bare timer,
i timer og minutter, eller i både timer, minutter og sekunder. De tre metodene kunne
vi isteden ha skrevet som én enkelt metode på følgende måte:
public int antSekunder(int ... tid) { int total = 0; //antallet som skal returneres int faktor = 3600; for (int antall : tid) { total += antall * faktor; faktor /= 60; } return total; }
Med denne definisjonen kunne vi skrevet metodekallene akkurat på samme måte som vi ville ha gjort med den opprinnelige versjonen der vi hadde tre overloadete metoder.
Elementene i en array kan være av en hvilken som helst datatype. De kan derfor også være av type array, slik at vi får en array av arrayer, eller det vi heller kaller en flerdimensjonal array. Det gjøres sjelden bruk av arrayer av større dimensjon enn 2. Vi begrenser oss derfor til dette. Når vi bruker todimensjonale arrayer, kan detaljene utformes på litt forskjellige måter. Vi skal først se på noen enkle eksempler for å belyse dette.
Vi skal opprette en tabell av desimaltall med 4 rader og 5 kolonner:
double[][] tabell = new double[ 4 ][ 5 ];
I en todimensjonal array har vi to indekser, der første indeks refererer
til (horisontal)rad (linje), mens andre indeks refererer til kolonne
(vertikalrad). Rader og kolonner er indeksert fra 0 og oppover. For eksempel vil
tabell[0][0]
referere til verdien som er på første rad og i første
kolonne, mens
tabell[2][3]
vil referere til verdien som er på tredje rad og i fjerde
kolonne.
Numeriske
arrayer initialiseres automatisk til tallverdi null, mens arrayer av datatype
definert av en klasse blir initialisert automatisk til pekerverdien null
.
Som for endimensjonale arrayer, er det imidlertid også for todimensjonale
arrayer mulig å initialisere ved bruk av initialiseringslister samtidig som
arrayen deklareres, som vist i følgende eksempel:
int[][] tabell = { { 1, 2, 3 }, { 4, 5, 6 } };
Arrayen blir opprettet radvis, slik at vi i dette tilfelle får
en array som på første rad inneholder tallene 1, 2 og 3, mens den på andre rad
har tallene 4, 5 og 6. Når arrayen blir opprettet på denne måten, angir vi
altså ingen dimensjoner og bruker heller ikke
new
-operatoren.
En todimensjonal array er altså egentlig en array av arrayer. Vi vet at
hver array i seg selv er et objekt og at arrayvariablene egentlig er pekere
til arrayobjektene. I tilfelle array av arrayer, betyr dette at i arrayen
tabell
som ble opprettet i eksempel 2, så vil
tabell[0]
være en peker til den endimensjonale arrayen som
utgjør første rad i den todimensonale arrayen vår. På tilsvarende måte vil
tabell[1]
være en peker til den endimensonale arrayen som
utgjør andre rad. Om vi ønsker det, kan vi faktisk bruke denne kunnskapen til
å bytte om radene i den todimensonale arrayen vår. Det kan vi oppnå ved å skrive
følgende kode:
int[] rad = tabell[0]; //Oppretter en ekstra peker til første rad. tabell[0] = tabell[1]; //Erstatter første rad med den andre. tabell[1] = rad; //Erstatter andre rad med den som opprinnelig var først.
I en todimensjonal array er det ikke noe krav om at de endimensjonale arrayene som utgjør radene skal være av samme lengde. Vi kan derfor ha tabeller med varierende radlengde. Følgende kode vil gi en array der første rad har lengde 3, mens andre rad har lengde 2:
int[][] varTabell = { { 1, 2, 3 }, { 4, 5 } };
I eksempel 1 ovenfor ble begge dimensjoner spesifisert da den todimensjonale arrayen ble opprettet. (I de andre eksemplene har vi brukt initialiseringslister.) Vi behøver ikke i første omgang spesifisere mer enn den første dimensjonen, det vil si den som angir antall rader. Den andre, altså den som angir antall kolonner, kan vi vente med. Dette er aktuelt å gjøre når vi skal opprette en todimensjonal array med varierende radlengde. Kode for dette kan se ut som i følgende eksempel, uttrykt i pseudo-kode:
int antRader = ...; int[][] fleksitabell = new int[ antRader ][]; //oppretter radene: for ( int i = 0; i < fleksitabell.length; i++ ) fleksitabell[ i ] = new int[ < lengde, dvs. antall kolonner for rad nummer i > ];
Når vi skal gjennomløpe en todimensjonal array, bruker vi vanligvis en
dobbel for
-løkke, der den ytre løkka gjennomløper radene, mens
den indre løkka gjennomløper hver enkelt rad. For arrayen i eksempel 4 kan
en slik gjennomløping se ut på følgende måte, uttrykt i pseudo-kode:
for ( int rad = 0; rad < fleksitabell.length; rad++ ) { for ( int kolonne = 0; kolonne < fleksitabell[ rad ].length; kolonne++ ) { < gjør et eller annet med fleksitabell[ rad ][ kolonne ] > } }
Vi skal lage et program som simulerer utlånstall i et bibliotek for alle
årets dager. Utlånstallene skal genereres som slumptall og lagres i en
todimensjonal array. (For enkelhets skyld bryr vi oss ikke om at på enkelte dager
vil utlånstallet i virkeligheten være null, fordi biblioteket er stengt.)
Arrayen har en rad for hver måned i året, altså 12 rader. Hver rad skal ha like
mange plasser som det er dager i vedkommende måned. (Vi ser bort fra skuddår.)
Dette kan vi få til ved å gå fram som det er vist i klassen Bokutlaan
nedenfor, der arrayen utlån
blir opprettet i klassens konstruktør.
Klassen inneholder metoden genererData
som gjennomløper arrayen
og legger inn et slumptall på hver plass. Metoden skrivUt
gjennomløper
også arrayen. Den tilføyer arrayverdiene i det tekstområde som den mottar som
parameter. Fila som inneholder klassen kan du laste ned ved å klikke på
følgende link:
Bokutlaan.java
.
1 import javax.swing.JTextArea; 2 import java.util.Random; 3 4 public class Bokutlaan 5 { 6 private int[] månedsdager = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 7 31, 30, 31 }; 8 private String[] månedsnavn = { "januar", "februar", "mars", "april", 9 "mai", "juni", "juli", "august", "september", "oktober", 10 "november", "desember" }; 11 private int[][] utlån; // skal inneholde simulerte utlånsdata 12 13 public Bokutlaan() 14 { 15 //oppretter 12 endimensjonale arrayer for månedene 16 utlån = new int[ månedsdager.length ][]; 17 //oppretter en array med riktig lengde for hver måned 18 for ( int i = 0; i < utlån.length; i++ ) 19 utlån[ i ] = new int[ månedsdager[ i ] ]; 20 } 21 22 //genererer utlånstall som slumptall 23 public void genererData() 24 { 25 Random generator = new Random(); 26 //gjennomløper den to-dimensjonale arrayen tabell og genererer 27 //verdier som slumptall 28 for ( int rad = 0; rad < utlån.length; rad++ ) 29 { 30 for ( int kolonne = 0; kolonne < utlån[ rad ].length; kolonne++ ) 31 utlån[ rad ][ kolonne ] = generator.nextInt( 500 ); 32 } 33 } 34 35 //lager tabellutskrift i tekstområde 36 public void skrivUt( JTextArea utskrift ) 37 { 38 //overskrift 39 utskrift.append( "Utlånstall\n\t" ); 40 for ( int dag = 1; dag <= månedsdager[0]; dag++ ) 41 utskrift.append( dag + ".\t" ); 42 utskrift.append( "\n" ); 43 //tabellutskrift 44 for ( int rad = 0; rad < utlån.length; rad++ ) 45 { 46 utskrift.append( månedsnavn[ rad ] + "\t" ); 47 for ( int kolonne = 0; kolonne < utlån[ rad ].length; kolonne++ ) 48 utskrift.append( utlån[ rad ][ kolonne ] + "\t" ); 49 50 utskrift.append( "\n" ); 51 } 52 } 53 }
Programmet
Tabelltest
som er gjengitt nedenfor, oppretter et Bokutlaan
-objekt og gjør
kall på dets metoder genererData
og skrivUt
.
Som parameter i den siste brukes et tekstområde. I tidligere eksempler har vi
opprettet et slikt ved å bruke default-konstruktør. Størrelsen vil da bli
bestemt automatisk av de data vi putter inn i det, det vil si av hvor mange linjer
vi legger inn, og hvor lang den lengste av disse er. I dette programmet vil
linjene være så lange at tekstområdet ville bli for bredt for skjermen. Vi
ønsker derfor å lage det smalere, og at vi isteden skal kunne bruke et
skrollefelt for å kunne se på de enkelte delene av tekstområdet.
Størrelse på et tekstområde kan vi bestemme ved å bruke to konstruktørparametre, én for ønsket antall rader, og én for ønsket antall kolonner, slik det er gjort i instruksjonen
JTextArea utskrift = new JTextArea( 15, 60 );
Kolonnebredde, her lik 60, må vi oppfatte som antall synlige
tegn på hver linje i tekstområdet. (De enkelte tegn vil vanligvis ha forskjellig
bredde, vi må derfor oppfatte tallet som et gjennomsnittstall.) Det er her
viktig å være klar over at konstruktørparametrene ikke begrenser antall linjer
det er mulig å legge inn i tekstområdet, eller hvor lang hver slik linje kan
være. Det parametrene bestemmer, er hvor mye som skal være synlig samtidig
på skjermen. Det som eventuelt er usynlig, er det da ønskelig å kunne se på
ved å foreta skrolling. Det oppnår vi ved at vi legger tekstområdet vårt inn i
en JScrollPane
slik det er gjort i følgende instruksjon:
JScrollPane skrollefelt = new JScrollPane( utskrift );
Legg merke til at det da er skrollefeltet vi legger inn som komponent i meldingsboksen, ikke tekstområdet:
JOptionPane.showMessageDialog( null, skrollefelt, "Utlånsdata", JOptionPane.PLAIN_MESSAGE );
Instruksjonen
utskrift.setEditable( false );
har som virkning at det ikke vil være mulig for brukeren å klikke seg inn i det tekststområde som vises og endre dets verdier. Dersom vi hadde utelatt denne instruksjonen, ville det vært mulig.
1 import javax.swing.JTextArea; 2 import javax.swing.JOptionPane; 3 import javax.swing.JScrollPane; 4 5 public class Tabelltest 6 { 7 public static void main( String[] args ) 8 { 9 Bokutlaan simulator = new Bokutlaan(); 10 JTextArea utskrift = new JTextArea( 15, 60 ); 11 utskrift.setEditable( false ); 12 utskrift.setTabSize(6); 13 JScrollPane skrollefelt = new JScrollPane( utskrift ); 14 15 simulator.genererData(); 16 simulator.skrivUt( utskrift ); 17 JOptionPane.showMessageDialog( null, skrollefelt, "Utlånsdata", 18 JOptionPane.PLAIN_MESSAGE ); 19 } 20 }
Følgende bilder viser resultatet av en kjøring av programmet. På det andre bildet er det skrollet til den del av tekstområdet som er lengst til høyre. Vi ser der tydelig at radene i arrayen har fått forskjellig lengde.
Copyright © Kjetil Grønning og Eva Hadler Vihovde, revidert 2014