compareTo nach mehreren Kriterien

ocsme

Top Contributor
Guten Tag,

hier sind zwei Klassen:

Java:
import java.util.*;

public class Gruppe implements Contact, Comparable<Contact> {
    String name;
    Set<Contact> eintraege;
    
    Gruppe() {
        
    }
    
    Gruppe(String n) {
        eintraege = new TreeSet<>();
        name=n;
    }
    
    public boolean add(Contact c) {
        if(c!=this)
            return eintraege.add(c) ;
        return false;
    }
    
    public String toString() {
        String ret = name;
        Iterator<Contact> it = entries.iterator();
        while(it.hasNext())
            ret+="\n"+it.next().toString();
        return ret;
    }

    @Override
    public int compareTo(Contact c) {
        if(c instanceof Gruppe) {
            return name.compareTo(((Gruppe) c).name);
        }
        return 1;
    }

}

Java:
import java.util.*;
public class Person implements Contact, Comparable<Contact>{
    
    String title;
    String firstName;
    String lastName;
    
    Person(String f, String l) {
        this(null,f,l);
    }
    
    Person(String t, String f, String l) {
        title=t;
        firstName=f;
        lastName=l;
    }
    
    public String toString() {
        return (title!=null) ?
                title+" "+((firstName!=null)?firstName+" "+((lastName!=null)?lastName:"") :"")
                : firstName!=null?firstName+" "+lastName:"";
    }

    @Override
    public int compareTo(Contact o) {
        if(o instanceof Person) {
            return lastName.compareTo(((Person) o).lastName);
        }
        else
            return -1;
    }
    
}

Das Interface
Java:
public interface Contact {
     // nothing here marker interface
}


Nun weiß ich nicht ob es mit einem Set überhaupt geht wenn nicht muss ich alles in eine Liste machen.
Was ich tun möchte ist, bei Person nach dem Nachnamen vergleichen sollten diese gleich sein soll nach dem Vornamen verglichen werden dann nach dem Vorname und danach nach dem Titel.

Somit soll z. B.:
Person p1 = new Person("Max", "Mustermann");
Person p2 = new Person("Dr", "Max", "Mustermann");

p1 und p2 sollen eingeordnet werden können.
Leider stehe ich hierbei irgendwie super auf dem Schlauch. Ich hab schon so etwas ausprobiert doch das ist ja Käse 😛
Java:
    @Override
    public int compareTo(Contact o) {
        if(o instanceof Person) {
            if(lastName.compareTo(((Person) o).lastName) != 0)
                return lastName.compareTo(((Person) o).lastName);
            else if(firstName.compareTo(((Person)o).firstName) != 0)
                    return firstName.compareTo((((Person)o).firstName));
                    else
                        return title.length()-((Person) o).title.length();
        }
        return -1;
    }

Kann mir da jemand eine Idee geben?
 
Dafür kannst du zB Comparator#thenComparing nutzen, Beispiele sollten sich dafür im JavaDoc finden lassen 🙂
 
meinst du das hier?
Java:
default Comparator<T> thenComparing(Comparator<? super T> other)

Ich will ja eigentlich nur Fragen wenn es einen Titel gibt vergleiche so gibt es keinen vergleiche so 😛

LG
 
meinst du das hier?
Java:
default Comparator<T> thenComparing(Comparator<? super T> other)

Ich will ja eigentlich nur Fragen wenn es einen Titel gibt vergleiche so gibt es keinen vergleiche so 😛
Genau dafür ist diese Methode da 😉

Du kannst das natürlich auch per Hand schreiben, wenn du das gerne möchtest, musst halt dann ==0 und !=0 entsprechend behandeln.
 
Du kannst das natürlich auch per Hand schreiben, wenn du das gerne möchtest, musst halt dann ==0 und !=0 entsprechend behandeln.

Das hatte ich vorhin versucht. Doch nicht wirklich so hin bekommen.
Werde es mir morgen genauer anschauen mit der Methode die du mir genannt hast vielleicht klappt das ja 🙂
Danke nochmals für die schnelle nette Hilfe 🙂

LG
 
Werde es mir morgen genauer anschauen mit der Methode die du mir genannt hast vielleicht klappt das ja 🙂

Ginge etwa so:
Java:
return comparing(Person::getLastName, nullsLast(naturalOrder()))
        .thenComparing(Person::getFirstName, nullsLast(naturalOrder()))
        .thenComparing(Person::getTitle, nullsLast(naturalOrder()))
        .compare(this, that);

Vergleicht erst lastName, bei Gleichheit firstName, und dann bei Gleichheit title.
Ein Attribut was null ist, ist dabei jeweils größer als eines, welches nicht null ist. Für den Title in jedem Fall nötig, bei den anderen könnte man es weglassen, wenn du sicherstellst, dass die nie null sind (dazu dann einfach das nullsLast(naturalOrder() weglassen.)

Das ganze könnte man auch noch in eine Konstante refactoren.
 
Vergleicht erst lastName, bei Gleichheit firstName, und dann bei Gleichheit title.
Ein Attribut was null ist, ist dabei jeweils größer als eines, welches nicht null ist. Für den Title in jedem Fall nötig, bei den anderen könnte man es weglassen, wenn du sicherstellst, dass die nie null sind (dazu dann einfach das nullsLast(naturalOrder() weglassen.)

Ja das mit dem Title == null macht mir bei der compareTo methode eben auch mega zu schaffen. Bekomme es einfach nicht hin.

Ich hätte gerne noch die Ordnung das erst Personen ausgegeben werden sollen dann die Gruppen. Das ganze habe ich ja auch soweit hin bekommen über die zwei Methoden:
In Person steht die compareTo Methode:
Java:
    @Override
    public int compareTo(Contact o) {
        if(o instanceof Person) {
                return lastName.compareTo(((Person) o).lastName);
        }
        else
            return -1;
    }

Da hier alles andere was keine Person ist -1 ist = Gruppen stehen die Gruppen eben hinter den Personen.
Wenn ich nun aber versuche den Title mit zu berücksichtigen bin ich einfach zu doof zu 🙁

Desweiteren verstehe ich jetzt auch nicht so ganz wie ich mit einem Comparator<Person> weiter kommen soll 🙁
Nimmt der TreeSet meine Datenstruktur zum speichern von Gruppen sowie Personen nicht die compareTo Methode zum vergleichen?

Müsste ich dann die Klasse Person als Comparator markieren und die Methode:
Java:
    @Override
    public int compare(Person o1, Person o2) {
        // TODO Auto-generated method stub
        return 0;
    }
implementieren? Die du oben ja schon hin geschrieben hast so gesehen?

Sorry doch das ganze habe ich noch nie gemacht 🙁

LG

Dachte mir jetzt so etwas in der Gruppe toString Methode:

Java:
public String toString() {
        Comparator<Contact> c = Comparator.comparing(Person::getLastName, nullsLast(naturalOrder()))
                .thenComparing(Person::getFirstName, nullsLast(naturalOrder()))
                .thenComparing(Person::getTitle, nullsLast(naturalOrder())));
        Collections.sort(entries, c);
        String ret = "";
        Iterator<Contact> it = entries.iterator();
        while(it.hasNext())
            ret+=it.next().toString()+"\n";
        ret+=name;
        return ret;
    }

naturalOrder() meckert er, kennt er nicht!

LG
 
Zuletzt bearbeitet:
Ja das mit dem Title == null macht mir bei der compareTo methode eben auch mega zu schaffen. Bekomme es einfach nicht hin.
Das ist auch recht kompliziert, du musst halt 3 Fälle abdecken, ((null,null), (null, nicht null), (nicht null, nicht null)), deshalb ist's deutlich einfacher, die schon existierenden Methoden dafür zu nutzen.


Desweiteren verstehe ich jetzt auch nicht so ganz wie ich mit einem Comparator<Person> weiter kommen soll 🙁
Du kannst den Comparator in compareTo nutzen, um this und die andere Person zu vergleichen.

Nimmt der TreeSet meine Datenstruktur zum speichern von Gruppen sowie Personen nicht die compareTo Methode zum vergleichen?
Ja, du kannst aber auch explizit einen Comparator angeben, der dann stattdessen genutzt wird. Grad in Hinblick auch Vererbung kann das sinnvoll sein. (zB, wenn du eine weitere Implementierung von Contact einführst - du müsstest dann jede bisherige Implementierung passend zu den anderen anpassen.)

Müsste ich dann die Klasse Person als Comparator markieren und die Methode:
Nein, solltest du nicht. Meinen Codeschnipsel kannst du in die bestehende compareTo einsetzen:

Java:
@Override
public int compareTo(Contact o) {
  if(o instanceof Person) {
   Person that = (Person) o;       
   return comparing(Person::getLastName, nullsLast(naturalOrder()))
     .thenComparing(Person::getFirstName, nullsLast(naturalOrder()))
     .thenComparing(Person::getTitle, nullsLast(naturalOrder()))
     .compare(this, that);
  } 
  else
    return -1;
}

(Oder halt dem TreeSet einen Comparator mitnehmen, das kann oft praktischer sein.)
 
Wo kommt nun die comparing Methode her?
Ich dachte die wäre im Comparator?
----
Das ganze Thema überfordert mich gerade sehr 🙁
-----

Die naturalOrder() Methode müsste nun noch in Person rein. Denn so geht es nicht 😀
Java:
            return comparing(Person::getLastName, nullsLast(Comparator.naturalOrder()))
 
Wo kommt nun die comparing Methode her?
Ich dachte die wäre im Comparator?
Ist sie auch. Du kannst die ganzen statischen Methoden importieren, dann musst du nicht jedes mal die Klasse davor setzen: import static java.util.Comparator.*;

In ausgeschrieben wäre das so:
Java:
return Comparator.comparing(Person::getLastName, Comparator.nullsLast(Comparator.naturalOrder()))
        .thenComparing(Person::getFirstName, Comparator.nullsLast(Comparator.naturalOrder()))
        .thenComparing(Person::getTitle, Comparator.nullsLast(Comparator.naturalOrder()))
        .compare(this, that);

Die naturalOrder() Methode müsste nun noch in Person rein. Denn so geht es nicht 😀
was meinst du damit? 😵
 
was meinst du damit? 😵

Ja ich dachte du meintest das man naturalOrder() selbst schreiben müsste.
Dann hab ich im Internet nach gelesen und gesehen das die Methode im Comparator ist.
Doch auf die Idee nullsLast im Comparator zu such bin ich nicht gekommen.

So geht es nun.

Java:
    @Override
    public int compareTo(Contact o) {
        if(o instanceof Person) {
            Person that = (Person) o; 
            return Comparator.comparing(Person::getLastName, Comparator.nullsLast(Comparator.naturalOrder()))
                    .thenComparing(Person::getFirstName, Comparator.nullsLast(Comparator.naturalOrder()))
                    .thenComparing(Person::getTitle, Comparator.nullsLast(Comparator.naturalOrder()))
                    .compare(this, that);
        }
        else
            return -1;
    }

Jetzt habe ich aber noch eine Frage, wieso Funktioniert das?
Denn Comparator.comparing "returns a Comparator<T> that compares by that sort key" wenn ich nicht ganz Falsch liege ist das ein Object und kein int Wert!
Ufff... das Thema ist ja super Umfangreich!!! Wie man das alles nur verstehen soll? 🙁

LG
 
Jetzt habe ich aber noch eine Frage, wieso Funktioniert das?
Denn Comparator.comparing "returns a Comparator<T> that compares by that sort key" wenn ich nicht ganz Falsch liege ist das ein Object und kein int Wert!
Ja, das gibt einen Comparator zurück - guck dir aber mal die letzte Zeile des Blocks an, dort wird compare aufgerufen, und damit this und that verglichen 😉
 
guck dir aber mal die letzte Zeile des Blocks an, dort wird compare aufgerufen, und damit this und that verglichen 😉

😀 haha die Wollte ich erst Weg lassen! Ich hab auch erst that gesucht! dachte das wäre ähnlich wie this eine Referenz auf irgendwas Java internes bis ich gesehen hab du hast Person that Object erstellt. 😀

Das Thema macht bei mir weiterhin Gehirnmatsch 😀
Ich bekomme ein Comparator zurück der den einen Wert verglichen hat nach dem obigen Kriterium dann vergleicht er diesen mit dem aufrufenden this.compare(that) <- so irgendwie?

Wie kann man das ganze den noch üben? Muss auch noch die Lambda Lernen und FunktionsInterfaces 🙂

LG
 

Zurück
Oben