Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C#: Kumpi tapa on parempi (CompareTo)

AtskaFin [16.03.2019 18:33:07]

#

Kumpi tapa on viisaampi, kun järjestetään listassa olevia olioita:

tapa1: luodaan luokkaan CompareTo() -metodi ja lisätään luokan nimen perään: IComparable<Object>

public int CompareTo(Opiskelija opiskelija)
        {
           if (getEtunimi().CompareTo(opiskelija.getEtunimi()) == 0)
            {
                if (getSukunimi().CompareTo(opiskelija.getSukunimi()) == 0) return 0;
                else if (getSukunimi().CompareTo(opiskelija.getSukunimi()) == 1) return 1;
                else return -1;
            }
            else if (getEtunimi().CompareTo(opiskelija.getEtunimi()) == 1) return 1;
            else return -1;
        }

Tapa2: Luodaan Sort() - metodia kutsuessa Lambda - lauseke:

opiskelijat.Sort((opiskelija1, opiskelija2) => opiskelija1.getEtunimi().CompareTo(opiskelija2.getEtunimi()) == 1 ? 1 : opiskelija1.getSukunimi().CompareTo(opiskelija2.getSukunimi()));

Metabolix [16.03.2019 19:01:42]

#

Ensimmäinen tapa (eli IComparable-rajapinnan toteuttaminen) on hyvä, jos luokalle voi määritellä jonkin yksiselitteisen järjestyksen. Esimerkiksi päivämäärät halutaan yleensä järjestää aikajärjestykseen. Mutta onko opiskelijoiden järjestys selvä: kuuluuko opiskelijoiden olla järjestettynä nimen mukaan, opiskelijanumeron mukaan vai esimerkiksi kurssiarvosanojen mukaan?

Lambda-funktio ei näytä tässä tapauksessa kovin viisaalta. Se on pitkä ja melko vaikea lukea. Lisäksi sen joutuisi kopioimaan jokaiseen kohtaan, jossa vastaavaa järjestystä tarvitaan. Toki kerran tehtävään järjestykseen sekin toimii.

Luultavasti paras vaihtoehto opiskelijoiden järjestämiseen on Comparison-delegaatin käyttö. Tässä etuna on myös, että metodin voi nimetä kuvaavasti.

public static int VertaaOpiskelijoitaNimenMukaan(Opiskelija a, Opiskelija b) {
	int tmp = a.sukunimi.CompareTo(b.sukunimi);
	return tmp != 0 ? tmp : a.etunimi.CompareTo(b.etunimi)
}
// ...
lista.Sort(VertaaOpiskelijoitaNimenMukaan);

Lisää esimerkkejä on Comparison-delegaatin dokumentaatiossa.

AtskaFin [16.03.2019 19:48:26]

#

Eli jos luokan oliot voi järjestää järkevällä tavalla niin kannattaa luoda CompareTo - metodi.

Toiseksi jos luokan voi järjestää monella tavalla niin kannattaa luoda Compare - delegaatteja.

Ja Lambda - lausetta kannattaa vältellä sort() - metodissa, varsinkin jos se on pitkä ja epäselvä.

Ps. Älysin tuosta dokumentaatiosta, että toistuvat (samanlaiset) foreach - lausekkeet voi tehdä metodilla. En ole sitä ennen älynnyt.

jalski [16.03.2019 20:14:56]

#

Jokin alla olevan tapainen taitaisi toimia myös ja on kohtuullisen luettava.

var lista = Opiskelijat.OrderBy(x => x.sukunimi).ThenBy(x => x.etunimi).ToList

AtskaFin [16.03.2019 20:31:30]

#

Eli lausekkeen...

(opiskelija1, opiskelija2) => opiskelija1.getEtunimi().CompareTo(opiskelija2.getEtunimi())

voi korvata seuraavalla:

x => x.getEtunimi();

Eli käsitinkö oikein: lausekkeen voi pilkkoa niin, että ensin asettaa OrderBy lambda - lausekkeen, ja jos arvot ovat samat, niin se katsoo seuraavan ThenBy - lausekkeen.

Grez [16.03.2019 21:08:41]

#

AtskaFin kirjoitti:

Eli lausekkeen...

(opiskelija1, opiskelija2) => opiskelija1.getEtunimi().CompareTo(opiskelija2.getEtunimi())

voi korvata seuraavalla:

x => x.getEtunimi();

Eli käsitinkö oikein: lausekkeen voi pilkkoa niin, että ensin asettaa OrderBy lambda - lausekkeen, ja jos arvot ovat samat, niin se katsoo seuraavan ThenBy - lausekkeen.

Et käsitä, eli ei voi. Huomaathan itsekin että tuossa Jalskin esimerkissä ei ole ainoastaan muutettu Sort(...) sisältöä, vaan vaihdettu käyttämään OrderBy(...):tä.

Mutta ihan yleisesti ottaen, mitä ihmeen Javaa noi esimerkkisi ovat???

C#:ssa ei ole tapana kirjoitella getFoo ja setFoo juttuja.

Jotain Javaa tms: (luultavasti sisältää kilon virheitä)

public class Opiskelija : IComparable
{
    string _etunimi;
    string _sukunimi;
    public string getSukunimi()
    {
        return _sukunimi;
    }
    public void setSukunimi(string value)
    {
        _sukunimi = value;
    }
    public string getEtunimi()
    {
        return _etunimi;
    }
    public void setEtunimi(string value)
    {
        _etunimi = value;
    }
    string Etunimi { get; set; }
    string Sukunimi { get; set; }

    public int CompareTo(Opiskelija opiskelija)
    {
        if (getEtunimi().CompareTo(opiskelija.getEtunimi()) == 0)
        {
            if (getSukunimi().CompareTo(opiskelija.getSukunimi()) == 0) return 0;
            else if (getSukunimi().CompareTo(opiskelija.getSukunimi()) == 1) return 1;
            else return -1;
        }
        else if (getEtunimi().CompareTo(opiskelija.getEtunimi()) == 1) return 1;
        else return -1;
    }
}

Sama C#:lla

public class Opiskelija : IComparable<Opiskelija>
{
    public string Etunimi { get; set; }
    public string Sukunimi { get; set; }

    public int CompareTo(Opiskelija obj)
    {
        int res = Etunimi.CompareTo(obj.Etunimi);
        return res == 0 ? Sukunimi.CompareTo(obj.Sukunimi) : res;
    }
}

Periaatteessa tapasi tehdä asia Sort:lla on oikein, mutta C#:lta näyttävä versio olisi

lista.Sort((a, b) => a.Etunimi == b.Etunimi
    ? a.Sukunimi.CompareTo(b.Sukunimi)
    : a.Etunimi.CompareTo(b.Etunimi));

Ja Metabolixin esittämä tapa on parempi jos sorttausta tarvitaan useammin kuin kerran.

Ja jalskin esittämä tapa on 3. tapa.

Ja tässä vielä 4. tapa:

lista = (from o in lista orderby o.Etunimi, o.Sukunimi select o).ToList();

Saattaa olla, että Sort (lajittelu saman taulukon sisällä) on tehokkaampi kuin nuo orderby systeemit, joissa luodaan uusi lista ja sijoitetaan se vanhan listan tilalle - tai sitten kääntäjän käsittelyn jälkeen mitään nopeuseroa ei ole.

Normaaliolosuhteissa pitäisin kuitenkin koodin luettavuutta tärkeämpänä kuin viimeiseen asti viilattua tehokkuutta. Toki sitten jos ollaan oikeasti tekemässä jotain nopeuskriittistä, niin voidaan alkaa tutkia mikä vaihtoehto todellisuudessa on nopein ja valita se.

AtskaFin [16.03.2019 23:10:42]

#

Sori siitä, että koodini näyttää javalta. Ohjelmoinnin mooc on tehty javalla, mutta olen harjoitellut siellä olevia tehtäviä ja samalla sekoittanut vähän javaa ja c#:ia.

Siis olen aina tehnyt ne tehtävät c#:lla

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta