Valmis rajapinta Comparable
compareTo
, jota käytetään olioiden vertailuun. Mikäli luokka toteuttaa rajapinnan Comparable, voidaan luokasta tehdyt oliot järjestää Javan valmiita järjestysalgoritmeja käyttäen.Comparable-rajapinnan vaatima compareTo-metodi saa parametrinaan olion, johon "this"-oliota verrataan. Mikäli olio on vertailujärjestyksessä ennen parametrina saatavaa olioa, tulee metodin palauttaa negatiivinen luku. Mikäli taas olio on järjestyksessä parametrina saatavan olion jälkeen, tulee metodin palauttaa positiivinen luku. Muulloin palautetaan luku 0. Tätä compareTo
-metodin avulla johdettua järjestystä kutsutaan luonnolliseksi järjestykseksi (natural ordering).
Tarkastellaan tätä kerhossa käyvää lasta tai nuorta kuvaavan luokan Kerholainen avulla. Jokaisella kerholaisella on nimi ja pituus. Kerholaisten tulee mennä syömään pituusjärjestyksessä, joten toteutetaan kerholaisille rajapinta Comparable
. Comparable-rajapinta ottaa tyyppiparametrinaan luokan, johon vertaus tehdään. Käytetään tyyppiparametrina samaa luokkaa Kerholainen
.
public class Kerholainen implements Comparable<Kerholainen> {
private String nimi;
private int pituus;
public Kerholainen(String nimi, int pituus) {
this.nimi = nimi;
this.pituus = pituus;
}
public String getNimi() {
return this.nimi;
}
public int getPituus() {
return this.pituus;
}
@Override
public String toString() {
return this.getNimi() + " (" + this.getPituus() + ")";
}
@Override
public int compareTo(Kerholainen kerholainen) {
if (this.pituus == kerholainen.getPituus()) {
return 0;
} else if (this.pituus > kerholainen.getPituus()) {
return 1;
} else {
return -1;
}
}
}
Rajapinnan vaatima metodi compareTo
palauttaa kokonaisluvun, joka kertoo vertausjärjestyksestä.
Koska compareTo()
-metodista riittää palauttaa negatiivinen luku, jos this
-olio on pienempi kuin parametrina annettu olio ja nolla, kun pituudet ovat samat, voidaan edellä esitelty metodi compareTo
toteuttaa myös seuraavasti.
@Override
public int compareTo(Kerholainen kerholainen) {
return this.pituus - kerholainen.getPituus();
}
Koska Kerholainen toteuttaa rajapinnan Comparable, voi listan kerholaisia järjestää virran sorted
-metodilla. Oikeastaan minkä tahansa Comparable
-rajapinnan toteuttavan luokan oliot voi järjestää virran sorted-metodilla. Huomaa kuitenkin, että virta ei järjestä alkuperäistä listaa, vaan vain virrassa olevat alkiot ovat järjestyksessä.
Mikäli ohjelmoija haluaa järjestää alkuperäisen listan, tähän kannattaa käyttää Collections
-luokan metodia sort
— luonnollisesti olettaen, että listalla olevat oliot toteuttavat rajapinnan Comparable
.
Kerholaisten järjestäminen on nyt suoraviivaista.
List<Kerholainen> kerholaiset = new ArrayList<>();
kerholaiset.add(new Kerholainen("mikael", 182));
kerholaiset.add(new Kerholainen("matti", 187));
kerholaiset.add(new Kerholainen("ada", 184));
kerholaiset.stream().forEach(k -> System.out.println(k);
System.out.println();
// tulostettavan virran järjestäminen virran sorted-metodilla
kerholaiset.stream().sorted().forEach(k -> System.out.println(k);
kerholaiset.stream().forEach(k -> System.out.println(k);
// listan järjestäminen Collections-luokan tarjoamalla sort-metodilla
Collections.sort(kerholaiset);
kerholaiset.stream().forEach(k -> System.out.println(k);
mikael (182) matti (187) ada (184)
mikael (182) ada (184) matti (187)
mikael (182) matti (187) ada (184)
mikael (182) ada (184) matti (187)
Järjestämismetodi lambda-lausekkeena
Oletetaan, että käytössämme on seuraava luokka Henkilo.
public class Henkilo {
private int syntymavuosi;
private String nimi;
public Henkilo(int syntymavuosi, String nimi) {
this.syntymavuosi = syntymavuosi;
this.nimi = nimi;
}
public String getNimi() {
return nimi;
}
public int getSyntymavuosi() {
return syntymavuosi;
}
}
Sekä henkilöolioita listalla.
ArrayList<Henkilo> henkilot = new ArrayList<>();
henkilot.add(new Henkilo("Ada Lovelace", 1815));
henkilot.add(new Henkilo("Irma Wyman", 1928));
henkilot.add(new Henkilo("Grace Hopper", 1906));
henkilot.add(new Henkilo("Mary Coombs", 1929));
Haluamme järjestää listan ilman, että henkilo-olion tulee toteuttaa rajapinta Comparable
.
Collections
metodille sort
että virran metodille sorted
voidaan antaa parametrina lambda-lauseke, joka määrittelee järjestämistoiminnallisuuden. Tarkemmin ottaen kummallekin metodille voidaan antaa Comparator-rajapinnan toteuttama olio, joka määrittelee halutun järjestyksen — lambda-lausekkeen avulla luodaan tämä olio.ArrayList<Henkilo> henkilot = new ArrayList<>();
henkilot.add(new Henkilo("Ada Lovelace", 1815));
henkilot.add(new Henkilo("Irma Wyman", 1928));
henkilot.add(new Henkilo("Grace Hopper", 1906));
henkilot.add(new Henkilo("Mary Coombs", 1929));
henkilot.stream().sorted((h1, h2) -> {
return h1.getSyntymavuosi() - h2.getSyntymavuosi();
}).forEach(h -> System.out.println(h.getNimi()));
System.out.println();
henkilot.stream().forEach(h -> System.out.println(h.getNimi()));
System.out.println();
Collections.sort(henkilot, (h1, h2) -> h1.getSyntymavuosi() - h2.getSyntymavuosi());
henkilot.stream().forEach(h -> System.out.println(h.getNimi()));
Ada Lovelace Grace Hopper Irma Wyman Mary Coombs
Ada Lovelace Irma Wyman Grace Hopper Mary Coombs
Ada Lovelace Grace Hopper Irma Wyman Mary Coombs
Merkkijonoja vertailtaessa voimme käyttää String-luokan valmista compareTo
-metodia. Metodi palauttaa sille annetun parametrina annetun merkkijonon sekä metodia kutsuvan merkkijonon järjestykstä kuvaavan kokonaisluvun.
ArrayList<Henkilo> henkilot = new ArrayList<>();
henkilot.add(new Henkilo("Ada Lovelace", 1815));
henkilot.add(new Henkilo("Irma Wyman", 1928));
henkilot.add(new Henkilo("Grace Hopper", 1906));
henkilot.add(new Henkilo("Mary Coombs", 1929));
henkilot.stream().sorted((h1, h2) -> {
return h1.getNimi().compareTo(h2.getNimi());
}).forEach(h -> System.out.println(h.getNimi()));
Ada Lovelace Grace Hopper Irma Wyman Mary Coombs
Järjestäminen useamman asian perusteella
Elokuva
public class Elokuva {
private String nimi;
private int julkaisuvuosi;
public Elokuva(String nimi, int julkaisuvuosi) {
this.nimi = nimi;
this.julkaisuvuosi = julkaisuvuosi;
}
public String getNimi() {
return this.nimi;
}
public int getJulkaisuvuosi() {
return this.julkaisuvuosi;
}
public String toString() {
return this.nimi + " (" + this.julkaisuvuosi + ")";
}
}
Luokka Comparator
tarjoaa järjestämistä varten kaksi oleellista metodia: comparing
ja thenComparing
. Metodille comparing
kerrotaan ensiksi verrattava arvo, ja metodille thenComparing
seuraavaksi verrattava arvo. Metodia thenComparing
voi käyttää monesti metodeja ketjuttaen, mikä mahdollistaa käytännössä rajattoman määrän vertailussa käytettäviä arvoja.
Kun järjestämme olioita, metodeille comparing
ja thenComparing
annetaan parametrina olion tyyppiin liittyvä metodiviite — järjestyksessä kutsutaan metodia ja vertaillaan metodin palauttamia arvoja. Metodiviite annetaan muodossa Luokka::metodi
. Alla olevassa esimerkissä tulostetaan elokuvat vuoden ja nimen perusteella järjestyksessä.
List<Elokuva> elokuvat = new ArrayList<>();
elokuvat.add(new Elokuva("A", 2000));
elokuvat.add(new Elokuva("B", 1999));
elokuvat.add(new Elokuva("C", 2001));
elokuvat.add(new Elokuva("D", 2000));
for (Elokuva e: elokuvat) {
System.out.println(e);
}
Comparator<Elokuva> vertailija = Comparator
.comparing(Elokuva::getJulkaisuvuosi)
.thenComparing(Elokuva::getNimi);
Collections.sort(elokuvat, vertailija);
for (Elokuva e: elokuvat) {
System.out.println(e);
}
A (2000) B (1999) C (2001) D (2000)
B (1999) A (2000) D (2000) C (2001)
Muistathan tarkistaa pistetilanteesi materiaalin oikeassa alareunassa olevasta pallosta!