1. Miten compareTo toimii tarkalleen?
Toimiiko se kuin matematiikan miinus -merkki. Huomasin, että vaihtojärjestämisessä oltiin kirjoitettu vain yksi kohta eri tavalla:
Koodi1: Ilman compareTo:ta
if (taulu[i] > taulu[j]) {...
Koodi2: CompareTo:n kanssa
if (taulu[i].compareTo(taulu[j]) > 0) {...
Nämä koodipätkät tarkoittavat samaa.
Matemaattisesti Koodi1:sestä saadaan:
Koodi3:
if (taulu[i] - taulu[j] > 0) {...
mikä on mielestäni oikein, koska taulun solut ovat numeroita.
2. Käytetäänkö siis Koodi3:sta vain siksi, että taulun solut ovat joskus kirjaimia?
Eli compareTo:n tarkoitus on ilmeisesti: sama kuin "miinus-merkin numeroille", mutta kirjaimille. Ehkä compareTo laskee sanojen kirjamien määrän ja näin vertaus on mahdollinen. Javadocin mukaan tämä vertaus tapahtuu "lexicographically".
Yksi vertailu (<, = tai >) sisältää vain kaksi vaihtoehtoa: tosi ja epätosi. Tällaisella vertailulla sen sijaan saadaan aikaan kaikki kolme vaihtoehtoa yhdellä funktiokutsulla, jolloin ohjelmasta tulee tehokkaampi. Jos kyseessä on esimerkiksi teksti ja vertailu tapahtuu aakkosellisesti, voi olla, että pitkää tekstiä joudutaan tutkimaan tuhansia merkkejä ennen eron löytymistä. Jos ohjelmassa olisi erikseen vertailut a < b ja a == b, jouduttaisiin nämä tuhat merkkiä tutkimaan molemmilla kerroilla. Kun käytetään funktiota compareTo, saadaan toinen kerta optimoitua pois: kun (jos) eroa löytyy, saadaan suoraan selville oikea järjestys.
Funktio toimii juuri tällä logiikalla, jonka itsekin havaitsit. Vertailuperuste voi olla mikä tahansa, mutta joka tapauksessa ajatuksena on, että
a < b
<=> a.compareTo(b) < 0
a > b
<=> a.compareTo(b) > 0
a == b
<=> a.compareTo(b) == 0
.
Vertailutapa tietenkin luokasta. Tekstin tapauksessa usein vertaillaan merkkien ASCII-arvoja, mikä vastaa aakkosellista vertailua, jos teksti koostuu vain isoista tai vain pienistä kirjaimista.
Optimoinnista vielä käytännön esimerkki:
if (a < b) { // vertailtiin kerran kaikkia tuhatta merkkiä } else if (a == b) { // vertailtiin uudestaan kaikkia tuhatta merkkiä } else { // nyt onneksi loogisesti a > b }
tulos = a.compareTo(b); // vertaillaan kerran if (tulos < 0) { // a < b } else if (tulos == 0) { // a == b } else { // a > b }
Kiitos vastauksesta!
Itse pidän yhden vertailun käytöstä (<, > ja =).
Onko tilanteita, joissa minun on pakko käyttää compareTo:ta?
Tuo optimointiesimerkki on hiukan harhaanjohtava, kun tuollaista tilannetta ei javassa oikein pääse tulemaan. <,= ja > operaattorit on määritelty vain luvuille eikä niitä siten voi käyttää stringien vertailuun. Lisäksi ne toimivat myös lukujen objekti wrappereille (Interger, Long yms.), jolloin virtuaalikone tekee luvuille automaattisen muunnoksen natiiveiksi lukutyypeiksi. Alkuperäisessä esimerkissä sinulla Hevonen oli varmaan taulukko Integer-olioita, joita voi vertailla sekä <,=,> -operattoreilla, että luokkan compareTo-metodilla.
compareTo-metodin oikea tarkoitus on siis mahdollistaa olioiden vertaileminen keskenään. Kaikille luokkien vertailuille pitää siis käyttää compareTo-metodia.
compareTo:n mielenkiintoisin ominaisuus on se, että voit toteuttaa omalle luokalle compareTo-metodin ja määritellä luokan perivän Comparable rajapinnan. Tällöin voit esimerkiksi käyttää suoraan omalla luokallasi java-apin mukana tulevia järjestely-luokkia tai tietorakenteita, jotka perustuvat olioiden järjestykseen. Oliosi järjestyvät automaattisesti haluamaasi järjestykseen, kunhan vain compareTo-metodissa osaat päättää onko parametrinä tuleva toinen olio pienempi (aikaisempi), yhtäsuuri tai suurempi (myöhempi) kuin nykyisen olio. Tämän päätöksen mukaan palauta esimerksi -1,0 tai 1 (mikä tahansa negatiivinen tai positiivinen luku käy ensimmäisessä ja viimeisessä tapauksessa). Tällöin voit tietenkin myös itse vertailla omia olioitasi compareTo metodin avulla.
FooBat kirjoitti:
Tuo optimointiesimerkki on hiukan harhaanjohtava, kun tuollaista tilannetta ei javassa oikein pääse tulemaan.
Joo, puhuin tosiaan hieman muiden kielten näkökulmasta. Vastaavia tapauksiahan ovat esimerkiksi C:n memcmp ja strcmp.
FooBat kirjoitti:
compareTo-metodissa osaat päättää onko parametrinä tuleva toinen olio pienempi (aikaisempi), yhtäsuuri tai suurempi (myöhempi) kuin nykyisen olio
ja näin sama asia lukee java-apissa
java-api kirjoitti:
- - a value less than 0 if this string is lexicographically less than the string argument; and a value greater than 0 if this string is lexicographically greater than the string argument.
Mitä tarkoitat, että olio A on aikaisempi kuin olio B?
Ehkä tarkoitat, että olio A ilmestyy koodissa ennen olio b:tä. Toisin sanoen ilmeisesti tarkoitat, että java -tulkki lukee olio A:n ennen olio B:tä.
hevonen kirjoitti:
Mitä tarkoitat, että olio A on aikaisempi kuin olio B?
Onko se noin vaikea lukea noista pätkistä, joita itsekin lainasit? Olio "luku 1" on pienempi eli numerojärjestykseen järjestetyssä taulukossa aikaisempi kuin "luku 2", ja näitä molempia suurempi eli myöhäisempi on esimerkiksi "luku 513". Onko vaikeaa? Voidaan määritellä vaikkapa luokka "Ihminen", jolla on ominaisuudet "pituus" ja "paino" ja funktio compareTo, joka laskee näistä painoindeksit ja vertailee niitä. Kun ihmiset sitten järjestetään, voi olio "Pekka" olla ensimmäinen, "Liisa" toinen ja "Matti" kolmas, jos compareTo antaa tällaisen tuloksen (eli jos painoindeksit menevät tässä järjestyksessä).
http://java.sun.com/javase/6/docs/api/java/lang/
Jos englanti taipuu, niin yllättäen dokumentaatiostakin voi olla apua, jos et tuota vielä oe lukennut. Eri luokat voivat siis toteuttaa tuon Comparable -rajapinnan toteuttamalla tuon CompareTo -metodin. Se on sitten jokaisen luokan oma asia, miten se sen tekee.
@Metabolix: Nyt ymmärsin. Eli Ihmis -esimerkissä vertaus tapahtuu painoindeksin suhteen. Näin henkilöt voidaan järjestää.
@Päärynämies: Kiitos linkistä! Linkki antoi vahvistuksen sille, että compareTo "is an equivalence relation on" such that the natural ordering is a total order on C. C is a given class in Java api.
Aihe on jo aika vanha, joten et voi enää vastata siihen.