Gyakorlati alapok III.
A Comparable interfész (objektumok alapértelmezett rendezése)
Az előző fejezetek némelyikében felhasználtunk olyan algoritmust is, amelyik
az objektum rendezéséért volt felelős. Ebben a fejezetben azt vizsgáljuk meg,
hogy a Java-rendszer miképpen valósítja ezt meg egységes keretek között!
A programozás-technikai trükk nagyon egyszerű:
alapértelmezésben mindegyik kollekció megvalósítja a Comparable interfészt, amelyik egyrészről tartalmazza a legáltalánosabb rendezési algoritmusokat, másrészről biztosít bizonyos rendezési szempontokat is.
Mit értünk az utóbbi alatt? Nyilvánvalóan más rendezési szempont alá tartozik egy Integer és egy String típus. A Comparable interfész felismeri ezt és az objektumot egy alapértelmezett sorrend szerint képes rendezni. Ez a beépített sorrend a következő:
-
Boolean - false < true
-
Character - előjelnélküli szám
-
Byte - előjeles szám
-
Integer - előjeles szám
-
BigInteger - előjeles szám
-
BigDecimal - előjeles szám
-
Long - előjeles szám
-
Short - előjeles szám
-
Float - előjeles szám
-
Double - előjeles szám
-
String - ABC szerinti
-
Date - időrendi
-
File - rendszerfüggő ABC szerinti (teljes név szerint)
-
CollationKey - ABC szerinti (helyi jellemzők szerint)
Rendezési varázsigénk pedig a következő:
Collections.sort(obj);
Ezután nincs más hátra, mint a fentieket leellenőrizzük egy List objektumon keresztül! Először vizsgáljunk meg egy Integer típusú objektumot! Az elemeket rendezetlenül adagoljuk az objektumba:
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Integer> lista = new ArrayList<Integer>();
lista.add(23);
lista.add(11);
lista.add(53);
lista.add(41);
lista.add(36);
System.out.println("Lista rendezés nélkül: " + lista);
Collections.sort(lista);
System.out.println("Lista
rendezéssel: " + lista);
}
}
Végeredmény:
Lista rendezés nélkül: [23, 11, 53, 41, 36]
Lista rendezéssel: [11, 23, 36, 41, 53]
Ha a listaelemeket és a típust átírjuk mondjuk String-re, a String típusra jellemző ABC szerinti rendezést kapunk. Ez a számok esetében egyenesen megtévesztő lehet, hiszen úgy tűnik, mintha számok szerinti rendezés történt volna:
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> lista = new ArrayList<String>();
lista.add("23");
lista.add("11");
lista.add("53");
lista.add("41");
lista.add("36");
System.out.println("Lista rendezés nélkül: " + lista);
Collections.sort(lista);
System.out.println("Lista rendezéssel: " + lista);
}
}
Végeredmény:
Lista rendezés nélkül: [23, 11, 53, 41, 36]
Lista rendezéssel: [11, 23, 36, 41, 53]
A turpisság rögtön kiderül, ha továbbvariáljuk a listaelemeket:
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> lista = new ArrayList<String>();
lista.add("23");
lista.add("11");
lista.add("53");
lista.add("414");
lista.add("36");
System.out.println("Lista rendezés nélkül: " + lista);
Collections.sort(lista);
System.out.println("Lista rendezéssel: " + lista);
}
}
Végeredmény:
Lista rendezés nélkül: [23, 11, 53, 414, 36]
Lista rendezéssel: [11, 23, 36, 414, 53]
Ekkor már nincs szám szerinti (növekvő) rendezés, illetve a végeredményből az is kiderül, hogy a rendezési elv valóban ABC szerinti, amely tábla garantáltan tartalmazza a következő szekvenciát is:
"1", "2", "3", "4", stb.
A fentiekben tehát értelmezésbeli hibákat követtünk el a típusokban. "Echte" String-objektumok rendezése valóban ABC-szerinti lesz:
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> lista = new ArrayList<String>();
lista.add("Béla");
lista.add("Andrea");
lista.add("Zsolt");
lista.add("Tamás");
lista.add("Nóra");
System.out.println("Lista rendezés nélkül: " + lista);
Collections.sort(lista);
System.out.println("Lista rendezéssel: " + lista);
}
}
Végeredmény:
Lista rendezés nélkül: [Béla, Andrea, Zsolt, Tamás, Nóra]
Lista rendezéssel: [Andrea, Béla, Nóra, Tamás, Zsolt]
Érdekes, bár haszontalan kísérlet a Boolean típus vizsgálata, de fontos észrevennünk, hogy minden a fenti szabályok szerint fog történni:
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Boolean> lista = new ArrayList<Boolean>();
lista.add(false);
lista.add(true);
lista.add(false);
lista.add(true);
lista.add(true);
System.out.println("Lista rendezés nélkül: " + lista);
Collections.sort(lista);
System.out.println("Lista rendezéssel: " + lista);
}
}
Végeredmény:
Lista rendezés nélkül: [false, true, false, true, true]
Lista rendezéssel: [false, false, true, true, true]
Próbáljuk meg a fentiek ellenőrzését egy kvázi bonyolultabb objektumtípussal is (Date - Dátum). 2 db Date típusú objektumba az aktuális dátumot tároljuk, de 5 másodperces késéssel, így nem lesznek teljesen ugyanazok. Minden az ismertetett alapelvek szerint történik: az 5 másodperccel későbbi dátum rendezés után természetesen hátrébb fog kerülni; a rendszer helyesen ismeri fel és rendezi ezt a bonyolult kollekciótípust is:
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Date> lista = new ArrayList<Date>();
Date dateCurrent1 = new Date();
try{
System.out.println("Várakozás 5
másodpercig...");
Thread.sleep(5000);
}catch(InterruptedException ie){}
Date dateCurrent2 = new Date();
lista.add(dateCurrent2);
lista.add(dateCurrent1);
System.out.println("Lista rendezés nélkül: " + lista);
Collections.sort(lista);
System.out.println("Lista rendezéssel: " + lista);
}
}
Végeredmény:
Várakozás 5 másodpercig...
Lista rendezés nélkül: [Tue Jul 16 21:48:17 CEST 2019, Tue Jul 16 21:48:12 CEST 2019]
Lista rendezéssel: [Tue Jul 16 21:48:12 CEST 2019, Tue Jul 16 21:48:17 CEST 2019]
Még mindig a Date osztálynál vagyunk: a jelenlegi (értsd: a mindenkori futtatási) dátum kivételével az összes, nála korábbi dátumot a Java-szabványok szerinti, 1970.01.01 00:00:00 GMT dátumtól számított milliszekundumokkal írjuk be. Minden a szabályok szerint történik:
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Date> lista = new ArrayList<Date>();
Date dateCurrent = new Date();
Date dateEarlier = new Date(1513353064025L);
Date dateMoreEarlier = new Date(1163313064025L);
lista.add(dateCurrent);
lista.add(dateEarlier);
lista.add(dateMoreEarlier);
System.out.println("Lista rendezés nélkül: " + lista);
Collections.sort(lista);
System.out.println("Lista rendezéssel: " + lista);
}
}
Végeredmény:
Lista rendezés nélkül: [Wed Jul 17 10:48:03 CEST 2019, Fri Dec 15 16:51:04 CET 2017, Sun Nov 12 07:31:04 CET 2006]
Lista rendezéssel: [Sun Nov 12 07:31:04 CET 2006, Fri Dec 15 16:51:04 CET 2017, Wed Jul 17 10:48:03 CEST 2019]
Olyan objektum esetén, amelyik nem valósítja meg a Comparable interfészt, a Collections.sort(objektum) hívásakor ClassCastException kivétel fog dobódni. Persze ez a metódushívás nagy baki, hiszen voltaképpen olyan dologgal próbáljuk az objektumot rendezni, amelyik nem is létezik, legalábbis nincs hozzácsatlakoztatva. A megoldás a Comparable interfész becsatlakoztatása és önálló összehasonlítási (vele rendezési) szempontok megadása. Az utóbbit a következő fejezetben nézzük meg.