Comparable og Comparator er interfaces som lar deg bestemme hvordan dine egne objekter skal rangeres i forhold til hverandre. Dette kommer spesielt godt til nytte når man bruker Collection-rammeverket, spesielt metoden Collections.sort. Disse interfacene krever en metode hver, henholds vis Compare(T o1, T o2) og compareTo(T o) hvor T er klassen som skal sammenlignes.
Comparable<T>
Comparable er det vanlig at klassen din implementerer. 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" er mindre enn det andre instanse. 0 hvis de er like, og et positivt tall dersom "this" er større enn det andre instanset.
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å medalje rangeringen trenger man en metode for å avgjøre dette.
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; } public int getGoldMedals() { return goldMedals; } public int getSilverMedals() { return silverMedals; } public int getBronzeMedals() { return bronzeMedals; } @Override public String toString(){ return name + "\t: g: "+ goldMedals+", s: " + silverMedals + ", b: " + bronzeMedals; } //@Override public int compareTo(OlympicCountry o) { if(this.getGoldMedals() != o.getGoldMedals()) // hvis guld 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 guld 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 som arver fra Collections og at denne listen inneholder et objekt som implementerer Comparable-interfacet.
Her ser dere en ny main-metode som bruker den allerede definerte klassen over OlympicCountry
public static void main(String[] args) { ArrayList<OlympicCountry> countries = new ArrayList<OlympicCountry>(); countries.add(new OlympicCountry("Norway", 5, 3, 7)); countries.add(new OlympicCountry("Sweden", 2, 9, 3)); countries.add(new OlympicCountry("Finland", 9, 2, 1)); countries.add(new OlympicCountry("Russia", 2, 9, 12)); countries.add(new OlympicCountry("Denmark", 7, 1, 6)); countries.add(new OlympicCountry("England", 5, 2, 10)); countries.add(new OlympicCountry("Canada", 5, 0, 4)); countries.add(new OlympicCountry("USA", 9, 2, 2)); print(countries); /* Printer ut: Norway : g: 5, s: 3, b: 7 Sweden : g: 2, s: 9, b: 3 Finland : g: 9, s: 2, b: 1 Russia : g: 2, s: 9, b: 12 Denmark : g: 7, s: 1, b: 6 England : g: 5, s: 2, b: 10 Canada : g: 5, s: 0, b: 4 USA : g: 9, s: 2, b: 2 Altså usortert, men ettersom klassen vår implementerer Comparable kan vi nå sortere instansene slik vi ønsker. */ Collections.sort(countries); print(countries); /* Sweden : g: 2, s: 9, b: 3 Russia : g: 2, s: 9, b: 12 Canada : g: 5, s: 0, b: 4 England : g: 5, s: 2, b: 10 Norway : g: 5, s: 3, b: 7 Denmark : g: 7, s: 1, b: 6 Finland : g: 9, s: 2, b: 1 USA : g: 9, s: 2, b: 2 Nå kan dere se at listen er sortert, men den er fortsatt ikke slik vi ønsker den, selvom compartTo klassen er helt rett. Les neste avsnitt for løsningen. } //for oversiktelig utskrift av ArrayListen public static void print(ArrayList<OlympicCountry> countries){ for (OlympicCountry country : countries) { System.out.println(country); } }
Comparator
Problemet over er at sorteringsalgoritmen automatisk sorterer i stigende rekkefølge. En enkel fiks 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 for et spesielt problem. Løsningen er å bruke en Comparator. I motsetning til Comparable , så er Comparator tiltenkt å 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 Collections.sort() kallet. Comparator-interfacet krever at du har metoden compare(o1,o2). Denne fungerer på samme måte som compareTo(), men tar en inn to argumenter istedet for at et argument sammenlignes med "this"
public class MedalComparer implements Comparator<OlympicCountry>{ //spesifiser hvilken klasse som skal sammenlignes som vanlig private boolean ascending; private boolean descending; //laget en custom constructor hvor du kan bestemme om du vil rangere stigende eller synkende public MedalComparer(String order){ ascending = order.toUpperCase().equals("ASCENDING"); descending = order.toUpperCase().equals("DESCENDING"); } //her sammenlignes to eksterne instanser med hverandre @Override public int compare(OlympicCountry o1, OlympicCountry o2) { OlympicCountry a; OlympicCountry b; //denne if-løkken tilegner objektene til a eller b, ettersom om man vil ha rangeringen stigende eller synkende if(descending){ a = o2; b = o1; } else{ a = o1; b = 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 klasse. Under kan du se hvordan den brukes i en main-metode, som også har referanser til den allerede definerte OlympicCountry-klassen og print metoden, som du finner litt lenger oppe.
public static void main(String[] args) { ArrayList<OlympicCountry> countries = new ArrayList<OlympicCountry>(); countries.add(new OlympicCountry("Norway", 5, 3, 7)); countries.add(new OlympicCountry("Sweden", 2, 9, 3)); countries.add(new OlympicCountry("Finland", 9, 2, 1)); countries.add(new OlympicCountry("Russia", 2, 9, 12)); countries.add(new OlympicCountry("Denmark", 7, 1, 6)); countries.add(new OlympicCountry("England", 5, 2, 10)); countries.add(new OlympicCountry("Canada", 5, 0, 4)); countries.add(new OlympicCountry("USA", 9, 2, 2)); Collections.sort(countries,new MedalComparer("descending")); print(countries); /* Her printes det nå: USA : g: 9, s: 2, b: 2 Finland : g: 9, s: 2, b: 1 Denmark : g: 7, s: 1, b: 6 Norway : g: 5, s: 3, b: 7 England : g: 5, s: 2, b: 10 Canada : g: 5, s: 0, b: 4 Russia : g: 2, s: 9, b: 12 Sweden : g: 2, s: 9, b: 3 */ }
Det anbefales og lese dokumentasjonen som er linket 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 og la klassen som skal sorteres implementere Comparable().
Sidetype | Ferdig |
---|---|
Teori | 90 |