Programmering

Sortering med Comparable og Comparator i Java

Programmører har ofte brug for at sortere elementer fra en database i en samling, matrix eller kort. I Java kan vi implementere den sorteringsalgoritme, vi ønsker med enhver type. Bruger Sammenlignelig interface og sammenligne med() metode, kan vi sortere i alfabetisk rækkefølge, Snor længde, omvendt alfabetisk rækkefølge eller tal. Det Komparator interface giver os mulighed for at gøre det samme, men på en mere fleksibel måde.

Uanset hvad vi vil gøre, skal vi bare vide, hvordan vi implementerer den korrekte sorteringslogik for den givne grænseflade og type.

Få kildekoden

Få koden til denne Java Challenger. Du kan køre dine egne tests, mens du følger eksemplerne.

Sortering af en Java-liste med et brugerdefineret objekt

I vores eksempel bruger vi den samme POJO, som vi hidtil har brugt til andre Java-udfordrere. I dette første eksempel implementerer vi den sammenlignelige grænseflade i Simpson klasse ved hjælp af Simpson i generisk type:

 klasse Simpson implementerer Comparable {String name; Simpson (strengnavn) {this.name = navn; } @ Override public int CompareTo (Simpson simpson) {returner dette.navn.kompareTo (simpson.navn); }} 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); }} 

Bemærk, at vi har tilsidesat sammenligningsmetoden () og sendt i en anden Simpson objekt. Vi har også tilsidesat toString () metode, bare for at gøre eksemplet lettere at læse.

Det toString metode viser al information fra objektet. Når vi udskriver objektet, vil output være det, der blev implementeret i toString ().

Metoden CompareTo ()

Det sammenligne med() metode sammenligner et givet objekt eller den aktuelle forekomst med et specificeret objekt for at bestemme rækkefølgen af ​​objekter. Her er et hurtigt kig på hvordan sammenligne med() arbejder:

Hvis sammenligningen vender tilbage

Derefter ...

  >= 1

  dette.navn> simpson.navn

  0

  this.name == simpson.name

  <= -1

  dette.navn <simpson.navn

Vi kan kun bruge klasser, der kan sammenlignes med sortere() metode. Hvis vi prøver at videregive en Simpson der ikke implementeres Sammenlignelig, vi modtager en kompileringsfejl.

Det sortere() metode bruger polymorfisme ved at passere ethvert objekt, der er Sammenlignelig. Objekter sorteres derefter som forventet.

Outputtet fra den forrige kode ville være:

 Bart Homer Lisa Marge 

Hvis vi ville vende ordren, kunne vi udveksle sortere() for en baglæns(); fra:

 Collections.sort (simpsons); 

til:

 Collections.reverse (simpsons); 

Implementering af baglæns() metode ville ændre den forrige output til:

 Marge Lisa Homer Bart 

Sortering af et Java-array

I Java kan vi sortere en matrix med den type, vi ønsker, så længe den implementerer Sammenlignelig interface. 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); }} 

Først og fremmest sortere() påkaldelse, er arrayet sorteret til:

 1 6 7 8 9 

I det andet sortere() påkaldelse, det er sorteret til:

 Homer Lisa 

Husk, at brugerdefinerede objekter skal implementeres Sammenlignelig for at blive sorteret, selv som en matrix.

Kan jeg sortere objekter uden sammenligning?

Hvis Simpson-objektet ikke blev implementeret Sammenlignelig, ville en ClassCastException blive kastet. Hvis du kører dette som en test, vil du se noget som følgende output:

 Fejl: (16, 20) java: ingen egnet metode fundet til sortering (java.util.List) metode java.util.Collections.sort (java.util.List) er ikke anvendelig (slutningsvariabel T har uforenelige grænser for ligestillingsbegrænsninger: com.javaworld.javachallengers.sortingcomparable.Simpson nedre grænser: java.lang.Comparable) metode java.util.Collections.sort (java.util.List, java.util.Comparator) kan ikke anvendes (kan ikke udlede typevariabel (s ) T (faktiske og formelle argumentlister er forskellige i længden)) 

Denne log kan være forvirrende, men rolig. Bare husk at en ClassCastException vil blive kastet for ethvert sorteret objekt, der ikke implementerer Sammenlignelig interface.

Sortering af et kort med TreeMap

Java API indeholder mange klasser til at hjælpe med sortering, herunder TreeMap. I eksemplet nedenfor bruger vi TreeMap at sortere tasterne i en Kort.

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

TreeMap bruger sammenligne med() metode implementeret af Sammenlignelig interface. Hvert element i det resulterende Kort sorteres efter nøglen. I dette tilfælde vil output være:

 Barney = øl, Homer = tv, Lenny = Carl, Moe = haglgevær 

Husk dog: hvis objektet ikke implementeres Sammenlignelig, a ClassCastException vil blive kastet.

Sortering af et sæt med TreeSet

Det Sæt interface er ansvarlig for lagring af unikke værdier, men når vi bruger TreeSet-implementeringen, sorteres indsatte elementer automatisk, når vi tilføjer 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); }} 

Output fra denne kode er:

 Barney, Homer, Lenny, Moe 

Igen, hvis vi bruger et objekt, der ikke er det Sammenlignelig, a ClassCastException vil blive kastet.

Sortering med komparator

Hvad hvis vi ikke ønsker at bruge det samme sammenligne med() metode fra POJO-klassen? Kunne vi tilsidesætte Sammenlignelig metode til at bruge en anden logik? Nedenfor er et eksempel:

 public class BadExampleOfComparable {public static void main (String ... args) {List characters = new ArrayList (); SimpsonCharacter homer = ny SimpsonCharacter ("Homer") {@ Override public int compareTo (SimpsonCharacter simpson) {returner this.name.length () - (simpson.name.length ()); }}; SimpsonCharacter moe = new SimpsonCharacter ("Moe") {@Override public int compareTo (SimpsonCharacter simpson) {returner 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 kode kompliceret og indeholder en masse gentagelser. Vi var nødt til at tilsidesætte sammenligne med() metode to gange for den samme logik. Hvis der var flere elementer, ville vi være nødt til at replikere logikken for hvert objekt.

Heldigvis har vi Comparator-grænsefladen, som lader os løsne sammenligne med() logik fra Java-klasser. Overvej det samme eksempel ovenfor omskrevet ved hjælp af Komparator:

 offentlig klasse 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 ()). DerefterComparingInt (character2 -> character2.name.length ()))); System.out.println (tegn); }} 

Disse eksempler viser den største forskel mellem Sammenlignelig og Komparator.

Brug Sammenlignelig når der er en enkelt, standard sammenligning for dit objekt. Brug Komparatornår du har brug for at omgå en eksisterende sammenligne med(), eller når du har brug for specifik logik på en mere fleksibel måde. Komparator løsner sorteringslogikken fra dit objekt og indeholder sammenligne med() logik inden for din sortere() metode.

Brug af Comparator med en anonym indre klasse

I dette næste eksempel bruger vi en anonym indre klasse til at sammenligne værdien af ​​objekter. En anonym indre klassei dette tilfælde er enhver klasse, der implementeres Komparator. Brug af det betyder, at vi ikke er bundet til at starte en navngivet klasse, der implementerer en grænseflade; i stedet implementerer vi sammenligne med() metode inde i den anonyme indre klasse.

 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); }} 

Mere om indre klasser

En anonym indre klasse er simpelthen enhver klasse, hvis navn ikke betyder noget, og som implementerer den grænseflade, vi erklærer. Så i eksemplet, det nye Komparator er faktisk instantiering af en klasse, der ikke har et navn, som implementerer metoden med den logik, vi ønsker.

Brug af Comparator med lambda-udtryk

Anonyme indre klasser er detaljerede, hvilket kan forårsage problemer i vores kode. I Komparator interface, kan vi bruge lambda-udtryk til at forenkle og gøre koden lettere at læse. For eksempel kan vi ændre dette:

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

Til dette:

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

Mindre kode og det samme resultat!

Output af denne kode ville være:

 Cyclops SpiderMan Wolverine Xavier 

Vi kunne gøre koden endnu enklere ved at ændre dette:

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

Til dette:

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

Lambda-udtryk i Java

Lær mere om lambda-udtryk og andre funktionelle programmeringsteknikker i Java.

Er de centrale Java-klasser sammenlignelige?

Mange centrale Java-klasser og objekter implementerer Sammenlignelig interface, hvilket betyder, at vi ikke behøver at implementere sammenligne med() logik for disse klasser. Her er et par velkendte eksempler:

Snor

 offentlig endelig klasse String implementerer java.io.Serializable, Comparable, CharSequence {... 

Heltal

 offentlig endelig klasse Heltal udvides Antal redskaber, der kan sammenlignes {... 

Dobbelt

 offentlig endelig klasse Dobbelt udvider Antal redskaber Sammenlignelig {... 

Der er mange andre. Jeg opfordrer dig til at udforske Java-kerneklasser for at lære deres vigtige mønstre og koncepter.

Tag den sammenlignelige interfaceudfordring!

Test det, du har lært, ved at finde ud af output fra følgende kode. Husk, du lærer bedst, hvis du løser denne udfordring for dig selv bare ved at studere den. Når du har nået et svar, kan du tjekke svaret nedenfor. Du kan også køre dine egne tests for fuldt ud at absorbere koncepterne.

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

Hvilken er output af denne kode?

 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