Average2
Dette eksemplet er, sammen med Average1, ment å utvide forståelsen av sammenhengen mellom hvilke metoder et objekt har og hvilken intern tilstand i form av variabler, det trenger.
Objekt-utforming
Denne gang ønsker vi oss et objekt som kan holde rede på både gjennomsnittet og median av en sekvens med desimaltall. Denne lille og uskyldige utvidelsen krever at vi revurderer hvilke data som må lagres i objektet:
-
Hva må objektet huske (på av data) for å kunne oppføre seg riktig?
Medianen er den midterste verdien når de sorteres (eller midt mellom de to midterste verdiene, når det er et like antall verdier). Nå må vi altså både huske alle verdiene og sortere dem.
Koding
Klassedeklarasjonen blir som for Average1
, men med Average2
som klassenavn.
Variabeldeklarasjoner
For å huske alle verdiene trenger vi en variabel som ikke representerer et enkelt desimaltall, men en (dynamisk) sekvens av flere desimaltall.
Dette får vi til ved å bruke List<Double>
(leses som "liste av Double") som type.
(Av tekniske grunner vi ikke går inn på her, så må vi bruke Double
og ikke double
som liste-element-type.
Tilsvarende må vi bruke Integer
istedet for int
, om vi ønsker en liste med heltall. Fra starten skal lista være tom, så vi bruker new ArrayList<Double>()
for å initialisere variablen.
En får da et ArrayList
-objekt, som er kodet slik at det passer når typen til variablen er List
.
En ArrayList
tilsvarer omtrent en Python-liste, men Java har (dessverre) ikke en egen syntaks for å gjøre en ArrayList
like lettvint i bruk.
En List
holder selv rede på hvor mange elementer den inneholder, så vi trenger ikke lenger count
-variablen:
List<Double> values = new ArrayList<Double>();
double sum = 0.0;
En viktig ting må nevnes: Både List
og ArrayList
er forhåndsdefinerte klasser i pakken java.util
, og derfor må vi egentlig skrive java.util.List>
og java.util.ArrayList
for å være presis,
f.eks. for å unngå sammenblanding med andre klasser med samme navn i andre pakker. Faktisk finnes det en klasse som heter java.awt.List
og koden over er sånn sett tvetydig: Hvilken (av de to) List
-klasse(ne) mener vi egentlig?
For å slippe å måtte skrive pakkenavnet foran klassenavnet overalt, så legger vi til import
-setninger under package
-setningen:
package stateandbehavior.average;
import java.util.List;
import java.util.ArrayList;
class Average2 {
// først kommer variabel-deklarasjoner (se over)
// så konstruktører
// deretter metoder
}
Med disse import
-setningene så vil all bruk av List
og ArrayList
(uten pakkenavn foran) tolkes som (å referere til) henholdsvis java.util.List
og java.util.ArrayList
.
List
blir en slags kortform for java.util.List
, slik at koden blir raskere å lese og skrive. Merk at hvis en nå faktisk ønsker å referere til java.awt.List
, så må en nå bruke det fulle klassenavnet med pakkenavnet foran.
Tip
|
Det kan virke tungvint å måtte legge inn import -setninger for alle klasser en bruker, men heldigvis har verktøy som Eclipse en snarvei for å gjøre det automatisk.
Første gang en skriver List (f.eks. i en variabel-deklarasjon) så trykker en <ctrl><space> når markøren står bakom, velger java.util.List fra menyen som dukker opp, og vips så legges import -setningen til øverst.
|
Objektdiagram
Opprettelse av et Average2
-objekt med new Average2()
gir følgende objekt(diagram):
Vi angir her en (tom) liste angis med []
. I objekttilstandsdiagrammet nedenfor vises lister med ett eller flere elementer.
Metoder
Klassen må forøvrig utvides på to måter, vi trenger en ny getMedian
-metode, og acceptValue
-metoden må legge den nye verdien inn i values
-lista på riktig plass:
double getMean() {
return sum / values.size(); // (1)
}
double getMedian() {
// hjelpevariabler
int count = values.size();
int middle = count / 2;
if (count % 2 == 0) // (2)
return (values.get(middle - 1) + values.get(middle)) / 2; // (3)
else
return values.get(middle); // (4)
}
void acceptValue(double value) {
int i = 0; // (5)
while (i < values.size()) { // (6)
if (values.get(i) > value) { // (7)
break; // (8)
}
i++; // (9)
}
values.add(i, value); // (10)
sum += value; // (11)
}
-
Deler sum på antall verdier, som en får fra liste-objektets size-metode.
-
Hvis antallet verdier er et partall (resten når vi deler på 2 er 0)
-
Returner gjennomsnittet av de to midterste verdiene
-
Ellers returnes den midterste verdien
-
Deklarasjon av posisjonstelleren
-
Løkka brukes til å finne posisjonen i lista der den nye verdien skal legges til.
Vi lari
-variablen løpe fra 0 til (men ikke med) lista sin lengde. -
Hvis verdien på posisjon
i
er større enn den nye, så har i kommet langt nok -
Løkka avbrytes med
break
-
Posisjonstelleren økes
-
Be lista om å skyte verdien inn i posisjon
i
-
Summen oppdateres som før
Testing med main-metoden og objekttilstandsdiagram
public static void main(String[] args) {
Average2 average = new Average2();
average.acceptValue(4.0);
average.acceptValue(5.0);
System.out.println("Verdier: " + average.values);
System.out.println("Gjennomsnitt: " + average.getMean());
System.out.println("Middelverdi: " + average.getMedian());
average.acceptValue(3.0);
System.out.println("Verdier: " + average.values);
System.out.println("Gjennomsnitt: " + average.getMean());
System.out.println("Middelverdi: " + average.getMedian());
average.acceptValue(0.0);
System.out.println("Verdier: " + average.values);
System.out.println("Gjennomsnitt: " + average.getMean());
System.out.println("Middelverdi: " + average.getMedian());
}