Denne siden forklarer hvordan grensesnittene java.util.Comparable og java.util.Comparator bidrar til sortering i Java.

Comparable og Comparator er grensesnitt som lar deg bestemme hvordan dine egne objekter skal rangeres i forhold til hverandre ved sortering. Dette kommer spesielt godt til nytte når man bruker Collection-rammeverket, spesielt metoden Collections.sort. Disse grensesnittene krever én metode hver, henholdsvis compareTo(T o) og  Compare(T o1, T o2) hvor T er klassen som skal rangeres/sammenlignes.

Comparable<T>

Comparable er ment å blir implementert av klassen som holder dataene som skal rangeres/sammenlignes. Den krever at du har definert metoden compareTo(T), hvor T er klassen som skal sammenlignes. compareTo-metoden skal returnere et negativt tall hvis "this" regnes som mindre enn argumentet (instans av samme klasse), 0 hvis de regnes som like, og et positivt tall dersom "this" regnes som er større enn argumentet.

Sett at du har en klasse som definerer et Land som deltar i OL. Klassen har felter for antall gull, sølv og bronsje. For at du skal kunne avgjøre hvilket land som kommer høyest opp på medaljerangeringen trenger man en metode for å avgjøre dette.

OlympicCountry
public class OlympicCountry implements Comparable<OlympicCountry> { //inne i klammene defineres hvilken klasse som skal sammenlignes i compareTo-metoden

	private String name;
	private int goldMedals;
	private int silverMedals;
	private int bronzeMedals;

	public OlympicCountry(String name, int goldMedals, int silverMedals, int bronzeMedals) {
		this.name = name;
		this.goldMedals = goldMedals;
		this.silverMedals = silverMedals;
		this.bronzeMedals = bronzeMedals;
	}
	// gettere
	public int getGoldMedals() {
		return goldMedals;
	}
	public int getSilverMedals() {
		return silverMedals;
	}
	public int getBronzeMedals() {
		return bronzeMedals;
	}

	@Override
	public String toString(){
		return name + ": "+ goldMedals + "-" + silverMedals + "-" + bronzeMedals;
	}

	@Override
	public int compareTo(OlympicCountry o) {
		if (this.getGoldMedals() != o.getGoldMedals()) // hvis gull er ulike, returnerer deres forskjell
			return this.getGoldMedals() - o.getGoldMedals();
		else if (this.getSilverMedals() != o.getSilverMedals()) //hvis sølv er ulike, returneres deres forskjell
			return this.getSilverMedals() - o.getSilverMedals();
		else
			return this.getBronzeMedals() - o.getBronzeMedals(); // hvis den er kommet hit er gull og sølv like, da returnerer den bronsje-forskjellen. Om den også er lik returneres 0, som indikerer at instansene er like gode.
	}

	public static void main(String[] args) {
		OlympicCountry china = new OlympicCountry("China", 2, 4, 1);
		OlympicCountry japan = new OlympicCountry("Japan", 2, 5, 1);

		System.out.println(china.compareTo(Japan)); // printer ut -1. Dette betyr at Japan er bedre enn Kina.
	}

Så hvorfor er dette så fantastisk. Jo, som nevnt over så kan man bruke det i forbindelse med sortering. Collections.sort() krever bare at du sender med en liste med objekter som implementerer Comparable-grensesnittet.

Her ser dere en ny main-metode som bruker den allerede definerte OlympicCountry-klassen.

public static void main(String[] args) {

		List<OlympicCountry> countries = new ArrayList<OlympicCountry>(Arrays.asList(
			new OlympicCountry("Norway", 5, 3, 7),
			new OlympicCountry("Sweden", 2, 9, 3),
			new OlympicCountry("Finland", 9, 2, 1),
			new OlympicCountry("Russia", 2, 9, 12),
			new OlympicCountry("Denmark", 7, 1, 6),
			new OlympicCountry("England", 5, 2, 10),
			new OlympicCountry("Canada", 5, 0, 4),
			new OlympicCountry("USA", 9, 2, 2)
		));
		System.out.println(countries);
		/*
		Printer ut: [Norway: 5-3-7, Sweden: 2-9-3, Finland: 9-2-1, Russia: 2-9-12, Denmark: 7-1-6, England: 5-2-10, Canada: 5-0-4, USA: 9-2-2]
		Altså usortert, men ettersom OlympicCountry-klassen implementerer Comparable kan vi nå sortere instansene slik vi ønsker.
		*/
		Collections.sort(countries);
		System.out.println(countries);
		/*
		[Sweden: 2-9-3, Russia: 2-9-12, Canada: 5-0-4, England: 5-2-10, Norway: 5-3-7, Denmark: 7-1-6, Finland: 9-2-1, USA: 9-2-2]
		Nå kan dere se at listen er sortert, men den er fortsatt ikke slik vi ønsker den, selv om compareTo-metoden er helt rett. Les neste avsnitt for løsningen.
		*/
 }

Comparator<T>

Problemet over er at sorteringsalgoritmen automatisk sorterer i stigende rekkefølge. En enkel løsning ville vært å bytte om på objektene i compareTo()-metoden, men dette ville vært feil ettersom Comparable skal gjenspeile klassens naturlige ordning og kan derfor ikke tilpasses et spesielt problem. Løsningen er å bruke en Comparator, som i motsetning til Comparable, er ment å implementeres av en annen klasse enn den som skal sorteres. Den kan da også ha friere regler på utfallet av sammenligninger. For å bruke en Comparator sendes den enkelt med som et andre argument til kallet til Collections.sort(). Comparator-grensesnittet krever at du har implementert metoden compare(o1,o2). Denne har samme logikk for returverdien som compareTo(), men tar en inn to argumenter istedet for at et argument sammenlignes med "this".

Comparator
public class MedalComparator implements Comparator<OlympicCountry> { //spesifiser hvilken klasse som skal sammenlignes som vanlig

	private boolean descending;
	
	// laget en custom konstruktør hvor du kan bestemme om du vil rangere stigende eller synkende
	public MedalComparator(boolean descending) {
		this.descending = descending;
	}

	// her sammenlignes to instanser med hverandre
	@Override
	public int compare(OlympicCountry o1, OlympicCountry o2) {

		OlympicCountry a = (descending ? o2 : o1);
		OlympicCountry b = (descending ? o1 : o2);
		
		// denne delen er helt lik som compareTo() metoden, a tilsvarer "this" og b tilsvarer "other"
		if (a.getGoldMedals() != b.getGoldMedals())
			return a.getGoldMedals() - b.getGoldMedals();
		else if (a.getSilverMedals() != b.getSilverMedals())
			return a.getSilverMedals() - b.getSilverMedals();
		else
			return a.getBronzeMedals() - b.getBronzeMedals();
	}
}

Ovenfor ser du hvordan en enkel Comparator-implementasjon. Under kan du se hvordan den brukes i en main-metode, som også har referanser til den allerede definerte OlympicCountry-klassen.

Using Comparator in ArrayList
public static void main(String[] args) {
		
		List<OlympicCountry> countries = new ArrayList<OlympicCountry>(Arrays.asList(
			new OlympicCountry("Norway", 5, 3, 7),
			new OlympicCountry("Sweden", 2, 9, 3),
			new OlympicCountry("Finland", 9, 2, 1),
			new OlympicCountry("Russia", 2, 9, 12),
			new OlympicCountry("Denmark", 7, 1, 6),
			new OlympicCountry("England", 5, 2, 10),
			new OlympicCountry("Canada", 5, 0, 4),
			new OlympicCountry("USA", 9, 2, 2)
		));		
		Collections.sort(countries, new MedalComparator(true));
		System.out.println(countries);
		/*
		Her printes det nå: [USA: 9-2-2, Finland: 9-2-1, Denmark: 7-1-6, Norway: 5-3-7, England: 5-2-10, Canada: 5-0-4, Russia: 2-9-12, Sweden: 2-9-3]
		*/
 
}

Det anbefales å lese dokumentasjonen som er linket til i innledningen for å forstå hvordan Java forventer at metodene skal fungere. Forskjellen er ikke så stor. Er du i tvil om hva du skal bruke vil det i de fleste tilfeller fungere å la klassen som skal sorteres implementere Comparable.

 

SidetypeFerdig
Teori90

 

 

 

  • No labels