Et eksempel med gjennomgang på todimensjonale matriser laget ved å innkapsle en ArrayList.

Det hender ofte at man ønsker å holde på verdier i et rutenett. Mange av oppgavene som handler om spill på denne wikien omhandler nettopp et spill hvor man har et diskret todimensjonalt brett. Man har da mange ruter med to koordinater (x og y) som kan holde på en verdi. Men hvordan holder man på disse verdiene i programmet? Den typiske måten man holder mange verdier på er å putte dem i en ArrayList. Men elementene i en ArrayList har bare ett "koordinat", (kalt index). Du tenker kanskje at det hadde vært fint om det fantes en slags "ArrayList" som tok to koordinater i stedet for én. Du prøver å søke opp en slik klasse i Javas standardbibliotek. Dessverre finner du ingen ting.

Du blir altså nødt til å finne din egen løsning. Ideelt hadde vi hatt en klasse Grid som lar oss: 1. Grid(int sizeX, int sizeY): Lage et Grid-objekt av en spesifikk størrelse. 2. setCell(int x, int y, value): Sette verdien til en celle i nettet. 3. getCell(int x, int y): Få tilbake verdien til en celle i nettet.

En måte å implementere dette på, er å ta i bruk godt kjente for oss ArrayList, og oversette mellom 2- og 1-dimpensjonale koordinater. Vi tenker oss at vi deler opp listen i en del for hver rad. Delene må åpenbart være (minst) størelsen av en rad for å få plass til en rad. Dette koker ned til formelen: index = y*x_max + x. Eller på java:

int calcIndex(int x, int y){
	return y*x_max + x;
}

Ved hjelp av denne metoden kan vi lage getCell og setCell:

int getCell(int x, int y){
	int index = calcIndex(x, y);
	return grid.get(index);
}

void setCell(int x, int y, int value){
	int index = calcIndex(x, y);
	grid.set(index, value);
}

En ting må legges merke til: Du kan ikke bruke set og get på ArrayList med indexer høyere enn antallet elementer du har add-et. Vi blir derfor nødt til å initialisere ArrayList-en med null-er:

for(int i = 0; i < (x_max * y_max); i++) {
	grid.add(null);
}

Satt sammen for vi kode som ser slik ut:

class Grid {
	private List<Integer> grid = new ArrayList<Integer>();
	private int x_max;
	private int y_max;

	public Grid(int x_max, int y_max){
		this.x_max = x_max;
		this.y_max = y_max;

		for(int i = 0; i < (x_max * y_max); i++){
			grid.add(null);
		}
	}

	public int getCell(int x, int y){
		int index = calcIndex(x, y);
		return grid.get(index);
	}

	public void setCell(int x, int y, int value){
		int index = calcIndex(x, y);
		return grid.set(index);
	}

	private calcIndex(int x, int y){
		return y*x_max + x;
	}
}

Dette er en start, men tar med kun det mest vesentlige, og koden har mangler. Her er forslag til hvordan man kan forbedre koden hvis man tar den i bruk:

  • Bedre feilhåndtering:Siden vi ikke gjør en sjekk om x og y er innenfor gyldig område, vil det skje to uting:
    1. Det vil være mulig (ved et uhell) å komme seg ned til en lavere rad hvis x settes større enn max_x.
    2. Siden vi gjør omregning til et internt format, vil IndexOutOfBoundsException som kastes av ArrayList vise til index-verdien vi får fra calcIndex.
    Dette forbedres ved å kaste en egen IndexOutOfBoundsException når det gir mening.
  • Dokumentasjon: Hvis man putter javadocs på klasser og metoder, blir det lett for deg selv og andre å slå opp bruken av denne klassen. Da holder det i elipse å flytte musepekeren over en refereansen til koden for få oversikt.
  • Andre typer enn int: Koden over en begrenset til å bare kunne lagre int-er. Hvis man bruker generics (parametrisering av typer), er det mulig å gjøre klassen helt generell i hendhold til hvilken type verdier som lagres.
  • No labels

2 Comments

  1. Burde det ikke stå

     return grid.set(index, value); 

    istedenfor

    return grid.set(index);

    i koden?

  2. Unknown User (hal)

    Du har (nesten) rett! Det må være grid.set(index, value), ja. I tillegg skal det ikke være return, for det første skal jo ikke metoden returnere noe (void) og set-metoden har heller ikke noen returverdi.

    Takk for tipset!