Programmering

Sortering med Comparable og Comparator i Java

Programmerere trenger ofte å sortere elementer fra en database i en samling, matrise eller kart. I Java kan vi implementere hvilken sorteringsalgoritme vi ønsker med hvilken som helst type. Bruker Sammenlignelig grensesnitt og sammenligne med() metode, kan vi sortere i alfabetisk rekkefølge, String lengde, omvendt alfabetisk rekkefølge eller tall. De Komparator grensesnitt tillater oss å gjøre det samme, men på en mer fleksibel måte.

Uansett hva vi vil gjøre, trenger vi bare å vite hvordan vi implementerer riktig sorteringslogikk for det angitte grensesnittet og typen.

Få kildekoden

Få koden for denne Java Challenger. Du kan kjøre dine egne tester mens du følger eksemplene.

Sortere en Java-liste med et tilpasset objekt

For eksempel vil vi bruke den samme POJO som vi har brukt for andre Java-utfordrere så langt. I dette første eksemplet implementerer vi det sammenlignbare grensesnittet i Simpson klasse, bruker Simpson i generisk type:

 klasse Simpson implementerer Comparable {String name; Simpson (strengnavn) {this.name = navn; } @Override public int compareTo (Simpson simpson) {returner this.name.compareTo (simpson.name); }} offentlig klasse SimpsonSorting {public static void main (String ... sortingWithList) {List simpsons = new ArrayList (); simpsons.add (ny SimpsonCharacter ("Homer")); simpsons.add (ny SimpsonCharacter ("Marge")); simpsons.add (ny SimpsonCharacter ("Bart")); simpsons.add (ny SimpsonCharacter ("Lisa")); Collections.sort (simpsons); simpsons.stream (). map (s -> s.name) .forEach (System.out :: print); Collections.reverse (simpsons); simpsons.stream (). forEach (System.out :: print); }} 

Vær oppmerksom på at vi har overstyrt sammenligningsmetoden () og sendt inn en annen Simpson gjenstand. Vi har også overstyrt toString () metode, bare for å gjøre eksemplet lettere å lese.

De toString metoden viser all informasjon fra objektet. Når vi skriver ut objektet, vil utdataene være det som ble implementert i toString ().

Metoden CompareTo ()

De sammenligne med() metoden sammenligner et gitt objekt eller den gjeldende forekomsten med et spesifisert objekt for å bestemme rekkefølgen på objekter. Her ser du raskt hvordan sammenligne med() virker:

Hvis sammenligningen kommer tilbake

Deretter ...

  >= 1

  dette.navn> simpson.navn

  0

  this.name == simpson.name

  <= -1

  this.name <simpson.name

Vi kan bare bruke klasser som er sammenlignbare med sortere() metode. Hvis vi prøver å passere en Simpson som ikke implementerer Sammenlignelig, vil vi motta en kompileringsfeil.

De sortere() metoden bruker polymorfisme ved å passere et hvilket som helst objekt Sammenlignelig. Objekter blir deretter sortert som forventet.

Utgangen fra forrige kode ville være:

 Bart Homer Lisa Marge 

Hvis vi ønsket å omgjøre ordren, kunne vi bytte sortere() for en omvendt(); fra:

 Collections.sort (simpsons); 

til:

 Collections.reverse (simpsons); 

Implementere omvendt() metoden vil endre forrige utgang til:

 Marge Lisa Homer Bart 

Sortere et Java-utvalg

I Java kan vi sortere en matrise med hvilken som helst type vi vil så lenge den implementerer Sammenlignelig grensesnitt. Her er et eksempel:

 public class ArraySorting {public static void main (String ... moeTavern) {int [] moesPints ​​= new int [] {9, 8, 7, 6, 1}; Arrays.sort (moesPints); Arrays.stream (moesPints) .forEach (System.out :: print); Simpson [] simpsons = nye Simpson [] {nye Simpson ("Lisa"), nye Simpson ("Homer")}; Arrays.sort (simpsons); Arrays.stream (simpsons) .forEach (System.out :: println); }} 

I det første sortere() påkalling, er matrisen sortert til:

 1 6 7 8 9 

I det andre sortere() påkallelse, den er sortert til:

 Homer Lisa 

Husk at tilpassede objekter må implementeres Sammenlignelig for å bli sortert, til og med som en matrise.

Kan jeg sortere objekter uten sammenlignbare?

Hvis Simpson-objektet ikke ble implementert Sammenlignelig, ville et ClassCastException kastes. Hvis du kjører dette som en test, vil du se noe som følgende utgang:

 Feil: (16, 20) java: ingen egnet metode funnet for sort (java.util.List) -metoden java.util.Collections.sort (java.util.List) er ikke aktuelt (inferensvariabel T har uforenlige grenser for likhetsbegrensninger: com.javaworld.javachallengers.sortingcomparable.Simpson nedre grenser: java.lang.Comparable) metode java.util.Collections.sort (java.util.List, java.util.Comparator) er ikke aktuelt (kan ikke utlede typevariabel (s ) T (faktiske og formelle argumentlister er forskjellige i lengde)) 

Denne loggen kan være forvirrende, men ikke bekymre deg. Bare husk at en ClassCastException vil bli kastet for ethvert sortert objekt som ikke implementerer Sammenlignelig grensesnitt.

Sortere et kart med TreeMap

Java API inneholder mange klasser for å hjelpe til med sortering, inkludert TreeMap. I eksemplet nedenfor bruker vi TreeMap å sortere nøkler i en Kart.

 public class TreeMapExample {public static void main (String ... barney) {Map simpsonsCharacters = new TreeMap (); simpsonsCharacters.put (ny SimpsonCharacter ("Moe"), "hagle"); simpsonsCharacters.put (ny SimpsonCharacter ("Lenny"), "Carl"); simpsonsCharacters.put (ny SimpsonCharacter ("Homer"), "TV"); simpsonsCharacters.put (ny SimpsonCharacter ("Barney"), "øl"); System.out.println (simpsonsCharacters); }} 

TreeMap bruker sammenligne med() metoden implementert av Sammenlignelig grensesnitt. Hvert element i det resulterende Kart sorteres etter nøkkelen. I dette tilfellet vil produksjonen være:

 Barney = øl, Homer = TV, Lenny = Carl, Moe = hagle 

Husk imidlertid: hvis objektet ikke implementeres Sammenlignelig, a ClassCastException vil bli kastet.

Sortere et sett med TreeSet

De Sett grensesnittet er ansvarlig for lagring av unike verdier, men når vi bruker TreeSet-implementeringen, blir innsatte elementer automatisk sortert når vi legger til dem:

 public class TreeSetExample {public static void main (String ... barney) {Set simpsonsCharacters = new TreeSet (); simpsonsCharacters.add (ny SimpsonCharacter ("Moe")); simpsonsCharacters.add (ny SimpsonCharacter ("Lenny")); simpsonsCharacters.add (ny SimpsonCharacter ("Homer")); simpsonsCharacters.add (ny SimpsonCharacter ("Barney")); System.out.println (simpsonsCharacters); }} 

Resultatet fra denne koden er:

 Barney, Homer, Lenny, Moe 

Igjen, hvis vi bruker et objekt som ikke er det Sammenlignelig, a ClassCastException vil bli kastet.

Sortering med komparator

Hva om vi ikke vil bruke det samme sammenligne med() metode fra POJO-klassen? Kan vi overstyre Sammenlignelig metode for å bruke en annen logikk? Nedenfor er et eksempel:

 public class BadExampleOfComparable {public static void main (String ... args) {List characters = new ArrayList (); SimpsonCharacter homer = new SimpsonCharacter ("Homer") {@Override public int compareTo (SimpsonCharacter simpson) {return this.name.length () - (simpson.name.length ()); }}; SimpsonCharacter moe = new SimpsonCharacter ("Moe") {@Override public int compareTo (SimpsonCharacter simpson) {return this.name.length () - (simpson.name.length ()); }}; characters.add (homer); characters.add (moe); Collections.sort (tegn); System.out.println (tegn); }} 

Som du kan se, er denne koden komplisert og inneholder mye repetisjon. Vi måtte overstyre sammenligne med() metode to ganger for samme logikk. Hvis det var flere elementer, måtte vi replikere logikken for hvert objekt.

Heldigvis har vi Comparator-grensesnittet, som lar oss koble fra sammenligne med() logikk fra Java-klasser. Tenk på det samme eksemplet ovenfor omskrevet med Komparator:

 public class GoodExampleOfComparator {public static void main (String ... args) {List characters = new ArrayList (); SimpsonCharacter homer = ny SimpsonCharacter ("Homer"); SimpsonCharacter moe = ny SimpsonCharacter ("Moe"); characters.add (homer); characters.add (moe); Collections.sort (tegn, (Comparator. ComparingInt (character1 -> character1.name.length ()) .thenComparingInt (character2 -> character2.name.length ()))); System.out.println (tegn); }} 

Disse eksemplene viser hovedforskjellen mellom Sammenlignelig og Komparator.

Bruk Sammenlignelig når det er en enkelt standard sammenligning for objektet ditt. Bruk Komparatornår du trenger å omgå et eksisterende sammenligne med(), eller når du trenger å bruke spesifikk logikk på en mer fleksibel måte. Komparator løsner sorteringslogikken fra objektet ditt og inneholder sammenligne med() logikk i din sortere() metode.

Bruke Comparator med en anonym indre klasse

I dette neste eksemplet bruker vi en anonym indre klasse for å sammenligne verdien på objekter. An anonym indre klasse, i dette tilfellet, er hvilken som helst klasse som implementeres Komparator. Å bruke det betyr at vi ikke er bundet til å sette i gang en navngitt klasse som implementerer et grensesnitt; i stedet implementerer vi sammenligne med() metode inne i den anonyme indre klassen.

 public class MarvelComparator {public static void main (String ... comparator) {List marvelHeroes = new ArrayList (); marvelHeroes.add ("SpiderMan"); marvelHeroes.add ("Wolverine"); marvelHeroes.add ("Xavier"); marvelHeroes.add ("Cyclops"); Collections.sort (marvelHeroes, new Comparator () {@Override public int compare (String hero1, String hero2) {return hero1.compareTo (hero2);}}); Collections.sort (marvelHeroes, (m1, m2) -> m1.compareTo (m2)); Collections.sort (marvelHeroes, Comparator.naturalOrder ()); marvelHeroes.forEach (System.out :: print); }} 

Mer om indre klasser

An anonym indre klasse er rett og slett enhver klasse hvis navn ikke betyr noe, og som implementerer grensesnittet vi erklærer. Så i eksemplet, det nye Komparator er faktisk instantiering av en klasse som ikke har et navn, som implementerer metoden med den logikken vi ønsker.

Bruke Comparator med lambdauttrykk

Anonyme indre klasser er verbose, noe som kan forårsake problemer i koden vår. I Komparator grensesnitt, kan vi bruke lambdauttrykk for å forenkle og gjøre koden lettere å lese. For eksempel kan vi endre dette:

 Collections.sort (vidunder, ny Comparator () {@ Override offentlig int sammenligne (String hero1, String hero2) {return hero1.compareTo (hero2);}}); 

til dette:

 Collections.sort (vidunder, (m1, m2) -> m1.compareTo (m2)); 

Mindre kode og samme resultat!

Utgangen av denne koden vil være:

 Cyclops SpiderMan Wolverine Xavier 

Vi kan gjøre koden enda enklere ved å endre denne:

 Collections.sort (vidunder, (m1, m2) -> m1.compareTo (m2)); 

til dette:

 Collections.sort (vidunder, Comparator.naturalOrder ()); 

Lambda-uttrykk i Java

Lær mer om lambdauttrykk og andre funksjonelle programmeringsteknikker i Java.

Er de viktigste Java-klassene sammenlignbare?

Mange kjerne Java-klasser og objekter implementerer Sammenlignelig grensesnitt, noe som betyr at vi ikke trenger å implementere sammenligne med() logikk for disse klassene. Her er noen kjente eksempler:

String

 offentlig sluttklasse String implementerer java.io.Serializable, Comparable, CharSequence {... 

Heltall

 offentlig sluttklasse Heltall utvider Antall redskaper Sammenlignbare {... 

Dobbelt

 offentlig endelig klasse Dobbelt utvider Antall redskaper Sammenlignelig {... 

Det er mange andre. Jeg oppfordrer deg til å utforske Java-kjerneklassene for å lære viktige mønstre og konsepter.

Ta den sammenlignbare grensesnittutfordringen!

Test det du har lært ved å finne ut resultatet av følgende kode. Husk at du lærer best hvis du løser denne utfordringen selv ved å studere den. Når du har nådd svaret, kan du sjekke svaret nedenfor. Du kan også kjøre dine egne tester for å absorbere konseptene fullt ut.

 public class SortComparableChallenge {public static void main (String ... doYourBest) {Set set = new TreeSet (); set.add (nye Simpson ("Homer")); set.add (nye Simpson ("Marge")); set.add (nye Simpson ("Lisa")); set.add (nye Simpson ("Bart")); set.add (nye Simpson ("Maggie")); Listeliste = ny ArrayList (); list.addAll (sett); Collections.reverse (liste); list.forEach (System.out :: println); } statisk klasse Simpson implementerer Comparable {String name; offentlig Simpson (strengnavn) {this.name = navn; } public int compareTo (Simpson simpson) {return simpson.name.compareTo (this.name); } offentlig streng til streng () {returner dette.navn; }}} 

Hva er utdataene fra denne koden?

 A) Bart Homer Lisa Maggie Marge B) Maggie Bart Lisa Marge Homer C) Marge Maggie Lisa Homer Bart D) Ubestemt 
$config[zx-auto] not found$config[zx-overlay] not found