Προγραμματισμός

Ταξινόμηση με συγκρίσιμο και συγκριτικό σε Java

Οι προγραμματιστές συχνά πρέπει να ταξινομούν στοιχεία από μια βάση δεδομένων σε μια συλλογή, πίνακα ή χάρτη. Στην Java, μπορούμε να εφαρμόσουμε όποιον αλγόριθμο ταξινόμησης θέλουμε με οποιονδήποτε τύπο. Χρησιμοποιώντας το Συγκρίσιμος διεπαφή και σύγκρισηΤο () μέθοδος, μπορούμε να ταξινομήσουμε χρησιμοποιώντας αλφαβητική σειρά, Σειρά μήκος, αντίστροφη αλφαβητική σειρά ή αριθμοί. ο Συγκριτής διεπαφή μας επιτρέπει να κάνουμε το ίδιο αλλά με πιο ευέλικτο τρόπο.

Ό, τι θέλουμε να κάνουμε, απλώς πρέπει να γνωρίζουμε πώς να εφαρμόσουμε τη σωστή λογική ταξινόμησης για τη δεδομένη διεπαφή και τύπο.

Λάβετε τον πηγαίο κώδικα

Λάβετε τον κωδικό για αυτό το Java Challenger. Μπορείτε να εκτελέσετε τις δικές σας δοκιμές ενώ ακολουθείτε τα παραδείγματα.

Ταξινόμηση λίστας Java με προσαρμοσμένο αντικείμενο

Για το παράδειγμά μας θα χρησιμοποιήσουμε το ίδιο POJO που χρησιμοποιήσαμε για άλλους Java Challengers μέχρι στιγμής. Σε αυτό το πρώτο παράδειγμα, εφαρμόζουμε το συγκρίσιμο περιβάλλον εργασίας στο Σίμπσον τάξη, χρησιμοποιώντας Σίμπσον στο γενικό τύπο:

 Η κλάση Simpson εφαρμόζει το συγκρίσιμο {String name; Simpson (Όνομα συμβολοσειράς) {this.name = name; } @Override public int membandingkanTo (Simpson simpson) {return this.name.compareTo (simpson.name); }} δημόσια τάξη SimpsonSorting {public static void main (String ... sortingWithList) {List simpsons = new ArrayList (); simpsons.add (νέος SimpsonCharacter ("Homer")); simpsons.add (νέος SimpsonCharacter ("Marge")); simpsons.add (νέος SimpsonCharacter ("Bart")); simpsons.add (νέος SimpsonCharacter ("Lisa")); Collections.sort (simpsons); simpsons.stream (). map (s -> s.name) .forEach (System.out :: print); Συλλογές. Αντίστροφη (simpsons); simpsons.stream (). forEach (System.out :: print); }} 

Λάβετε υπόψη ότι έχουμε παρακάμψει τη μέθοδο σύγκρισης () και περάσαμε σε άλλη Σίμπσον αντικείμενο. Παρακάμψαμε επίσης το toString () μέθοδος, απλώς για να γίνει ευκολότερο να διαβαστεί το παράδειγμα.

ο toString Η μέθοδος δείχνει όλες τις πληροφορίες από το αντικείμενο. Όταν εκτυπώνουμε το αντικείμενο, η έξοδος θα είναι ό, τι υλοποιήθηκε toString ().

Η μέθοδος σύγκρισηςTo ()

ο σύγκρισηΤο () Η μέθοδος συγκρίνει ένα δεδομένο αντικείμενο ή την τρέχουσα παρουσία με ένα καθορισμένο αντικείμενο για να προσδιορίσει τη σειρά των αντικειμένων. Ακολουθεί μια γρήγορη ματιά στο πώς σύγκρισηΤο () έργα:

Εάν η σύγκριση επιστρέψει

Επειτα ...

  >= 1

  this.name> simpson.name

  0

  this.name == simpson.name

  <= -1

  this.name <simpson.name

Μπορούμε να χρησιμοποιήσουμε μόνο τάξεις που είναι συγκρίσιμες με το είδος() μέθοδος. Εάν προσπαθήσουμε να περάσουμε ένα Σίμπσον αυτό δεν εφαρμόζεται Συγκρίσιμος, θα λάβουμε ένα σφάλμα συλλογής.

ο είδος() Η μέθοδος χρησιμοποιεί πολυμορφισμό περνώντας οποιοδήποτε αντικείμενο είναι Συγκρίσιμος. Στη συνέχεια, τα αντικείμενα θα ταξινομηθούν όπως αναμενόταν.

Η έξοδος από τον προηγούμενο κώδικα θα ήταν:

 Bart Homer Lisa Marge 

Εάν θέλαμε να αντιστρέψουμε τη σειρά, θα μπορούσαμε να ανταλλάξουμε το είδος() για ένα ΑΝΤΙΣΤΡΟΦΗ(); από:

 Collections.sort (simpsons); 

προς την:

 Συλλογές. Αντίστροφη (simpsons); 

Ανάπτυξη του ΑΝΤΙΣΤΡΟΦΗ() Η μέθοδος θα άλλαζε την προηγούμενη έξοδο σε:

 Marge Lisa Homer Bart 

Ταξινόμηση ενός πίνακα Java

Στην Java, μπορούμε να ταξινομήσουμε έναν πίνακα με οποιονδήποτε τύπο θέλουμε εφ 'όσον εφαρμόζει το Συγκρίσιμος διεπαφή. Ακολουθεί ένα παράδειγμα:

 δημόσια τάξη 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 = νέο Simpson [] {new Simpson ("Lisa"), new Simpson ("Homer")}; Arrays.sort (simpsons); Arrays.stream (simpsons) .forEach (System.out :: println); }} 

Κατά την πρώτη είδος() επίκληση, ο πίνακας ταξινομείται σε:

 1 6 7 8 9 

Στο δεύτερο είδος() επίκληση, ταξινομείται σε:

 Όμηρος Λίζα 

Λάβετε υπόψη ότι πρέπει να εφαρμοστούν προσαρμοσμένα αντικείμενα Συγκρίσιμος προκειμένου να ταξινομηθεί, ακόμη και ως πίνακας.

Μπορώ να ταξινομήσω αντικείμενα χωρίς δυνατότητα σύγκρισης;

Εάν το αντικείμενο Simpson δεν είχε εφαρμογή Συγκρίσιμος, ένα ClassCastException θα πεταχτεί. Εάν το εκτελέσετε ως δοκιμή, θα δείτε κάτι σαν την ακόλουθη έξοδο:

 Σφάλμα: (16, 20) java: δεν βρέθηκε κατάλληλη μέθοδος για τη μέθοδο sort (java.util.List) com.javaworld.javachallengers.sortingcomparable.Simpson κάτω bounds: java.lang.Comparable) μέθοδο java.util.Collections.sort (java.util.List, java.util.Comparator) δεν εφαρμόζεται (δεν μπορεί να συναχθεί τύπος-μεταβλητή (s ) T (οι πραγματικές και επίσημες λίστες επιχειρημάτων διαφέρουν σε μήκος)) 

Αυτό το αρχείο καταγραφής μπορεί να προκαλεί σύγχυση, αλλά μην ανησυχείτε. Απλώς λάβετε υπόψη ότι α ClassCastException θα ρίξει για οποιοδήποτε αντικείμενο ταξινομημένο που δεν υλοποιεί το Συγκρίσιμος διεπαφή.

Ταξινόμηση χάρτη με TreeMap

Το Java API περιλαμβάνει πολλές κατηγορίες για βοήθεια με τη διαλογή, συμπεριλαμβανομένου του TreeMap. Στο παρακάτω παράδειγμα, χρησιμοποιούμε Χάρτης δέντρου για να ταξινομήσετε τα κλειδιά σε ένα Χάρτης.

 δημόσια τάξη TreeMapExample {public static void main (String ... barney) {Map simpsonsCharacters = new TreeMap (); simpsonsCharacters.put (νέο SimpsonCharacter ("Moe"), "κυνηγετικό όπλο"); simpsonsCharacters.put (νέο SimpsonCharacter ("Lenny"), "Carl"); simpsonsCharacters.put (νέο SimpsonCharacter ("Homer"), "τηλεόραση"); simpsonsCharacters.put (νέο SimpsonCharacter ("Barney"), "μπύρα"); System.out.println (simpsonsCharacters); }} 

Χάρτης δέντρου χρησιμοποιεί το σύγκρισηΤο () μέθοδο που εφαρμόζεται από το Συγκρίσιμος διεπαφή. Κάθε στοιχείο στο προκύπτον Χάρτης ταξινομείται από το κλειδί του. Σε αυτήν την περίπτωση, η έξοδος θα είναι:

 Barney = μπύρα, Όμηρος = τηλεόραση, Lenny = Carl, Moe = κυνηγετικό όπλο 

Θυμηθείτε, ωστόσο: εάν το αντικείμενο δεν υλοποιείται Συγκρίσιμος, ένα ClassCastException θα πετάξει.

Ταξινόμηση ενός συνόλου με TreeSet

ο Σειρά Η διεπαφή είναι υπεύθυνη για την αποθήκευση μοναδικών τιμών, αλλά όταν χρησιμοποιούμε την εφαρμογή TreeSet, τα παρεχόμενα στοιχεία θα ταξινομηθούν αυτόματα καθώς τα προσθέτουμε:

 δημόσια τάξη TreeSetExample {public static void main (String ... barney) {Set simpsonsCharacters = new TreeSet (); simpsonsCharacters.add (νέο SimpsonCharacter ("Moe")); simpsonsCharacters.add (νέο SimpsonCharacter ("Lenny")); simpsonsCharacters.add (νέο SimpsonCharacter ("Homer")); simpsonsCharacters.add (νέο SimpsonCharacter ("Barney")); System.out.println (simpsonsCharacters); }} 

Η έξοδος από αυτόν τον κωδικό είναι:

 Barney, Homer, Lenny, Moe 

Και πάλι, αν χρησιμοποιήσουμε ένα αντικείμενο που δεν είναι Συγκρίσιμος, ένα ClassCastException θα πετάξει.

Ταξινόμηση με Συγκριτή

Τι γίνεται αν δεν θέλαμε να χρησιμοποιήσουμε το ίδιο σύγκρισηΤο () μέθοδο από την τάξη POJO; Θα μπορούσαμε να παρακάμψουμε το Συγκρίσιμος μέθοδος για να χρησιμοποιήσετε μια διαφορετική λογική; Ακολουθεί ένα παράδειγμα:

 δημόσια τάξη BadExampleOfComparable {public static void main (String ... args) {List χαρακτήρες = new ArrayList (); SimpsonCharacter homer = νέο SimpsonCharacter ("Homer") {@Override public int membandingkanTo (SimpsonCharacter simpson) {return this.name.length () - (simpson.name.length ()); }} SimpsonCharacter moe = νέο SimpsonCharacter ("Moe") {@Override public int membandingkanTo (SimpsonCharacter simpson) {return this.name.length () - (simpson.name.length ()); }} models.add (homer); character.add (moe); Collections.sort (χαρακτήρες); System.out.println (χαρακτήρες); }} 

Όπως μπορείτε να δείτε, αυτός ο κώδικας είναι περίπλοκος και περιλαμβάνει πολλή επανάληψη. Έπρεπε να παρακάμψουμε το σύγκρισηΤο () μέθοδος δύο φορές για την ίδια λογική. Εάν υπήρχαν περισσότερα στοιχεία θα έπρεπε να αναπαράγουμε τη λογική για κάθε αντικείμενο.

Ευτυχώς, έχουμε τη διεπαφή σύγκρισης, η οποία μας επιτρέπει να αποσυνδέουμε το σύγκρισηΤο () λογική από τάξεις Java. Εξετάστε το ίδιο παράδειγμα παραπάνω που ξαναγράφηκε χρησιμοποιώντας Συγκριτής:

 δημόσια τάξη GoodExampleOfComparator {public static void main (String ... args) {Λίστα χαρακτήρων = νέο ArrayList (); SimpsonCharacter homer = νέο SimpsonCharacter ("Όμηρος"); SimpsonCharacter moe = νέο SimpsonCharacter ("Moe"); character.add (homer); character.add (moe); Collections.sort (χαρακτήρες, [Comparator. ComparingInt (character1 -> character1.name.length ()) .thenComparingInt (character2 -> character2.name.length ()))); System.out.println (χαρακτήρες); }} 

Αυτά τα παραδείγματα δείχνουν την κύρια διαφορά μεταξύ Συγκρίσιμος και Συγκριτής.

Χρήση Συγκρίσιμος όταν υπάρχει μία, προεπιλεγμένη σύγκριση για το αντικείμενο σας. Χρήση Συγκριτήςόταν πρέπει να επιλύσετε ένα υπάρχον σύγκρισηΤο ()ή όταν πρέπει να χρησιμοποιήσετε συγκεκριμένη λογική με πιο ευέλικτο τρόπο. Συγκριτής αποσυνδέει τη λογική ταξινόμησης από το αντικείμενο σας και περιέχει το σύγκρισηΤο () λογική μέσα στο δικό σας είδος() μέθοδος.

Χρήση του Συγκριτή με μια ανώνυμη εσωτερική τάξη

Σε αυτό το επόμενο παράδειγμα, χρησιμοποιούμε μια ανώνυμη εσωτερική κλάση για να συγκρίνουμε την αξία των αντικειμένων. Ενα ανώνυμη εσωτερική τάξη, σε αυτήν την περίπτωση, είναι οποιαδήποτε κατηγορία που εφαρμόζει Συγκριτής. Χρησιμοποιώντας το σημαίνει ότι δεν είμαστε υποχρεωμένοι να δημιουργούμε μια ονομαστική τάξη που εφαρμόζει μια διεπαφή. αντ 'αυτού, εφαρμόζουμε το σύγκρισηΤο () μέθοδο μέσα στην ανώνυμη εσωτερική τάξη.

 δημόσια τάξη MarvelComparator {public static void main (String ... perbandingan) {List marvelHeroes = new ArrayList (); marvelHeroes.add ("SpiderMan"); marvelHeroes.add ("Wolverine"); marvelHeroes.add ("Xavier"); marvelHeroes.add ("Cyclops"); Collections.sort (marvelHeroes, new Comparator () {@Override public int membandingkan (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); }} 

Περισσότερα για τις εσωτερικές τάξεις

Ενα ανώνυμη εσωτερική τάξη είναι απλώς οποιαδήποτε κλάση του οποίου το όνομα δεν έχει σημασία και η οποία εφαρμόζει τη διεπαφή που δηλώνουμε. Έτσι στο παράδειγμα, το νέο Συγκριτής είναι στην πραγματικότητα η παρουσία μιας τάξης που δεν έχει όνομα, η οποία εφαρμόζει τη μέθοδο με τη λογική που θέλουμε.

Χρήση του Συγκριτή με εκφράσεις λάμδα

Οι ανώνυμες εσωτερικές τάξεις είναι ρητές, οι οποίες μπορούν να προκαλέσουν προβλήματα στον κώδικά μας. Στο Συγκριτής διεπαφή, μπορούμε να χρησιμοποιήσουμε τις εκφράσεις lambda για να απλοποιήσουμε και να κάνουμε τον κώδικα ευκολότερο στην ανάγνωση. Για παράδειγμα, θα μπορούσαμε να το αλλάξουμε αυτό:

 Collections.sort (marvel, new Comparator () {@Override public int dibandingkan (String hero1, String hero2) {return hero1.compareTo (hero2);}}) 

σε αυτό:

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

Λιγότερος κωδικός και το ίδιο αποτέλεσμα!

Η έξοδος αυτού του κώδικα θα είναι:

 Cyclops SpiderMan Wolverine Xavier 

Θα μπορούσαμε να κάνουμε τον κώδικα ακόμη πιο απλό αλλάζοντας αυτό:

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

σε αυτό:

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

Εκφράσεις λάμδα στην Ιάβα

Μάθετε περισσότερα για τις εκφράσεις λάμδα και άλλες λειτουργικές τεχνικές προγραμματισμού στην Java.

Είναι οι βασικές τάξεις Java συγκρίσιμες;

Πολλές βασικές κλάσεις Java και αντικείμενα εφαρμόζουν το Συγκρίσιμος διεπαφή, που σημαίνει ότι δεν χρειάζεται να εφαρμόσουμε το σύγκρισηΤο () λογική για αυτές τις τάξεις. Ακολουθούν μερικά γνωστά παραδείγματα:

Σειρά

 δημόσια τελική κλάση String υλοποιεί java.io.Serializable, Comparable, CharSequence {... 

Ακέραιος αριθμός

 δημόσια τελική κλάση Integer επεκτείνει Συγκρίσιμα αριθμητικά εργαλεία {… 

Διπλό

 δημόσια τελική κλάση Διπλές επεκτάσεις αριθμών Συγκρίσιμα {... 

Υπάρχουν πολλοί άλλοι. Σας ενθαρρύνω να εξερευνήσετε τα βασικά μαθήματα Java για να μάθετε τα σημαντικά πρότυπα και τις έννοιες τους.

Πάρτε την πρόκληση της συγκρίσιμης διεπαφής!

Δοκιμάστε τι έχετε μάθει υπολογίζοντας την έξοδο του παρακάτω κώδικα. Θυμηθείτε, θα μάθετε καλύτερα αν λύσετε αυτήν την πρόκληση για τον εαυτό σας, απλά μελετώντας την. Μόλις λάβετε μια απάντηση, μπορείτε να ελέγξετε την απάντηση παρακάτω. Μπορείτε επίσης να εκτελέσετε τις δικές σας δοκιμές για να απορροφήσετε πλήρως τις έννοιες.

 δημόσια τάξη SortComparableChallenge {public static void main (String ... doYourBest) {Set set = new TreeSet (); set.add (νέο Simpson ("Όμηρος")); set.add (νέο Simpson ("Marge")); set.add (νέο Simpson ("Lisa")); set.add (νέο Simpson ("Bart")); set.add (νέο Simpson ("Maggie")); Λίστα λιστών = νέο ArrayList (); list.addAll (σύνολο); Collections.reverse (λίστα); list.forEach (System.out :: println); } η στατική τάξη Simpson εφαρμόζει συγκρίσιμο {String name; δημόσια Simpson (όνομα συμβολοσειράς) {this.name = name; } public int membandingkanTo (Simpson simpson) {return simpson.name.compareTo (this.name); } δημόσια συμβολοσειρά toString () {return this.name; }}} 

Ποια είναι η έξοδος αυτού του κώδικα;

 A) Bart Homer Lisa Maggie Marge B) Maggie Bart Lisa Marge Homer C) Marge Maggie Lisa Homer Bart D) Αόριστη