Comparable & Comparator

b1zarRe

Bekanntes Mitglied
Hi,

ja ich habe nach dem Thema hier schon gesucht und was gefunden, doch
leider erklärt mir das noch nicht meine Frage, also:

Mit Comparable kann ich ja nun N Instanzen einer Klasse sortieren.(indem
ich die compareTo(Object o) korrekt überschreibe)... alles klar.

Aber was ist der Vorteil von Comparator?
Ist das lediglich, dass man damit verschiedenstartige Sortierungen vornehmen kann,
zb.: bei einer Klasse Student einen Comparator fürs Namen Sortieren und einen
fürs Matrikelnr studieren?

Ist das der einzige Vorteil?

ODER: Lassen sich mit einem Comparator auch zwei gaanz verschiedene Typen vergleichen.
zb.: eine Instanz der Klasse Student und eine Instanz der Klasse Artikel...? ist das machbar...?
ja, ich habe es ausprobiert und leider kommt bei mir immer die Fehlermeldung
das ich Klasse A nicht zu Klasse B casten kann, obwohl ich das Casting im Comparator
richtig vorgenommen habe....?

Das ganze Vorhaben habe ich mit 2 verschiedenen Objekten versucht und in eine (genericfreie)
ArrayList gepackt und dann mit Collections.sort(arraylist, new MeinComparator());
probiert..?

Mache ich was falsch, oder ist das dafür nicht vorgesehen?
 
Bei
Code:
Comparable
steckt die Logik wie zwei Intanzen soritert werden direkt in der Klasse. Beim
Code:
Comparator
wird diese Logik ausgelagert, beide Varianten können Sinn machen.

ODER: Lassen sich mit einem Comparator auch zwei gaanz verschiedene Typen vergleichen.
zb.: eine Instanz der Klasse Student und eine Instanz der Klasse Artikel...? ist das machbar...?
Nein, das kann nicht funktionieren. Comparator hat nur einen generischen Parameter. Du kannst also nur Ts mit Ts vergleichen.
 
Technisch möglich ist es. Man KANN in einen Comparator natürlich sowas schreiben wie
Java:
String name0 = null;
String name1 = null;
if (object0 instanceof Article) name0 = ((Article)object0).getArticleName();
if (object0 instanceof Person) name0 = ((Person)object0).getPersonName();
if (object1 instanceof Article) name1 = ((Article)object1).getArticleName();
if (object1 instanceof Person) name1 = ((Person)object1).getPersonName();
return name0.compareTo(name1);

Für SINN ist eine Programmiersprache zum Glück nicht zuständig 😀

Für "Comparable" ist explizit gefordert, dass sie (nur?) mutually comparable sein muss.
 
Leider ist mir der Unterschied noch nicht wirklich klar..., und jetzt auch im Lesen des "Handbuch der Java Programmierens" zitiere ich: (Seite 204) "(...) Auch Objekte unterschiedlicher Klassen können problemlos miteinander verglichen werden, sofern compareTo dazu in der Lage ist. So ist es leicht vorstellbar, dass sowohl Autos als auch Fussballplätze und Papierblätter(und eventuell noch Äpfel und Birnen) auf der basis ihrer Grundfläche miteinander verglichen werden, denn diese Maßzahl hat für alle beteiligten Klassen eine Bedeutung".

Nun deshalb nochmal die Frage für dumme wie mich 😀

1. Comparable -> Benutzen um A) zwei Objekte EINER Klasse zu vergleichen undOder B) Objekte vergleichen zwischen diversen Klassen && A) ?
2. Comparator -> Sinnvoll zum Erzeugen von diversen eignen Comparator Klasse, welche je nach Zweck eingesetzt werden können... zb. einen für Sortierung nach Preis, einen für Artikelnr. etc. -> Nur für Instanzen einer Klasse oder auch für beliebige Klassen???
 
Etwas mehr Kontext wäre da hilfreich. So, wie das dort beschrieben ist, könnte man es im schlimmsten Fall so verstehen, wie im oben geposteten Codeschnipsel, aber das macht natürlich keinen Sinn!

Vermutlich bezieht es sich nur darauf, dass man Objekte verschiedener Klassen vergleichen kann, wenn sie eine gemeinsame Basisklasse (bzw. ein gemeinsames Interface) haben, das das Vergleichskriterium festlegt. Im Beispiel oben könnte es z.B. sowas geben wie
Java:
interface NameOwner 
{
    String getName();
}

class Person implements NameOwner, Comparable<NameOwner>
{
    @Override
    public int compareTo(NameOwner o)
    {
        return 0;
    }

    @Override
    public String getName()
    {
        return null;
    }
    
}

class Artikel implements NameOwner, Comparable<NameOwner>
{
    @Override
    public int compareTo(NameOwner o)
    {
        return 0;
    }

    @Override
    public String getName()
    {
        return null;
    }
    
}

class Main
{
    public static void main(String[] args)
    {
        Person person = new Person();
        Artikel artikel = new Artikel();
        
        artikel.compareTo(person); // Geht
    }
}

Analog dazu könnte man auch einen Comparator erstellen, der zwei "NameOwner"-Objekte vergleicht.

Der Unterschied zwischen Comparable und Comparator ist eigentlich in erster Linie die Frage, WO die Vergleichbarkeit definiert wird: IN der Klasse oder außerhalb. Comparable verwendet man i.a. dann, wenn es eine inhärente Ordnung auf den Objekten gibt (Integer, String...).

Es gibt aber IMHO einen wichtigen Grund, im allgemeinen (für den öffentlichen Teil der API) Comparatoren zu verwenden: Wenn man Comparable-Objekte hat, kann man auf triviale Weise einen Comparator erstellen, der diese Comparable-Objekte vergleicht. Umgekehrt geht das nicht: Wenn eine Methode ewartet, dass man ihr Comparable-Objekte gibt (damit sie sie sortieren kann), aber die Objekte, die man hat, SIND einfach nicht Comparable, dann hat man verloren, und muss mit irgendwelchen Wrapper-Klassen rummurksen oder so...

Ich persönlich verwende Comparable sehr selten, und eigentlich nie in einer öffentlichen API, sondern wenn überhaupt dann in irgendwelchen (package) privaten Klassen, die nur intern nach einem festen Kriterium sortiert werden müssen.
 
Comparable verwendet man i.a. dann, wenn es eine inhärente Ordnung auf den Objekten gibt (Integer, String...).
Bzw. wenn es eine strukturverträgliche Totalordnung gibt, siehe geordneter Körper.

Es gibt aber IMHO einen wichtigen Grund, im allgemeinen (für den öffentlichen Teil der API) Comparatoren zu verwenden: Wenn man Comparable-Objekte hat, kann man auf triviale Weise einen Comparator erstellen, der diese Comparable-Objekte vergleicht. Umgekehrt geht das nicht: Wenn eine Methode ewartet, dass man ihr Comparable-Objekte gibt (damit sie sie sortieren kann), aber die Objekte, die man hat, SIND einfach nicht Comparable, dann hat man verloren, und muss mit irgendwelchen Wrapper-Klassen rummurksen oder so...
Ja, ich finde die Java-Lösung mit Comparable und Comparator auch Murks. Es wäre besser, wenn es dort so gemacht worden wäre wie mit Iterable und Iterator. Dann wäre Comparable (mit einer Methode comparator()) nur eine Garantie, dass es einen Comparator gibt; und die eigentlichen Implementierungen wären immer nur in Comparator-Klassen zu finden.

Mit equals() ist es imo noch schlimmer, man kann sich ja mehrere verschiedene Äquivalenzrelationen für dieselbe OO-Klasse von Objekten vorstellen.

… und gleich sind wir bei funktionaler Programmierung. 😀

Ark
 
Bzw. wenn es eine strukturverträgliche Totalordnung gibt, siehe geordneter Körper.

Auch diese würde man ggf. ändern können, wie du ja selbst sagst (und als ich das und den darauf folgenden Abschnitt gelesen habe, dachte ich auch gleich, das Beispiel anbringen zu können, dass man selbst Gleichheit (equals) gerne "von außen" definieren können wollte, aber dann hast du es schon selbst geschrieben 😀 )

Es wäre besser, wenn es dort so gemacht worden wäre wie mit Iterable und Iterator. Dann wäre Comparable (mit einer Methode comparator()) nur eine Garantie, dass es einen Comparator gibt; und die eigentlichen Implementierungen wären immer nur in Comparator-Klassen zu finden.

Im Moment verstehe ich (vielleicht wegen Schlaf- aka Koffeinmagel) nicht, wo da der Vorteil wäre: Man würde doch wieder das Kriterium an die Klasse selbst binden :bahnhof: Am "flexibelsten" im oben angedeuteten Sinne wäre eigentlich, wenn es "Comparable" gar nicht gäbe, aber natürlich wäre das oft unpraktisch, und für alles "praktischere" stößt man schnell an sprachliche Grenzen...
 
Im Moment verstehe ich (vielleicht wegen Schlaf- aka Koffeinmagel) nicht, wo da der Vorteil wäre: Man würde doch wieder das Kriterium an die Klasse selbst binden :bahnhof: Am "flexibelsten" im oben angedeuteten Sinne wäre eigentlich, wenn es "Comparable" gar nicht gäbe, aber natürlich wäre das oft unpraktisch, und für alles "praktischere" stößt man schnell an sprachliche Grenzen...
Ja, das Problem ist wohl, dass man in Interfaces keine statischen Methoden deklarieren kann. Dann könnte man nämlich so was schreiben wie hier:
Java:
	public ComparatorBeispiel(Collection<? extends E> collection){
		this(collection, ((Comparable) E).comparator());
	}
	
	public ComparatorBeispiel(Collection<? extends E> collection, Comparator<? super E> comparator){
		// usw.
		this.comparator = comparator;
		// usf.
	}
(Mit dem Generics-Kram bin ich mir nicht so sicher, ich hoffe aber, das klar ist, was gemeint ist.)

Dass das wiederum nicht geht, hat sicherlich irgendeine Bewandtnis, die mir gerade nicht einfallen mag (wahrscheinlich irgendwas mit dem Typsystem, Vererbung etc.). Auf jeden Fall könnte man sich viele Tests der Form [c]if (comparator != null)[/c], wie sie in z.B. TreeMap vorkommen, sparen.

(Es kann natürlich auch sein, dass ich gerade Sachen komplizierte mache, als sie sind. 😳 )

Ark
 
> Dann könnte man nämlich so was schreiben wie hier:

das geht auch jetzt schon wie von Marco13 geschrieben:
ein Comparator, der nur compareTo() der Klasse aufruft, ist schnell geschrieben, kein Aufwand


----

ich finde die Tendenzen hier nicht grundsätzlich gut, mit den gleichen Argumenten kann man auch jede andere
Funktionalität einer Klasse/ eines Interfaces herunterreden, wozu z.B. toString(), wieso nicht separat einen StringCreator?

aber Sortieren ist durchaus ein Spezialfeld, Comparator schon etabliert und wichtig,
und die genannte Alternative, Wrapper um jedes zu sortierende Element, ist schon eine hart Nuss,
durchaus verständlich
 
ich finde die Tendenzen hier nicht grundsätzlich gut, mit den gleichen Argumenten kann man auch jede andere
Funktionalität einer Klasse/ eines Interfaces herunterreden, wozu z.B. toString(), wieso nicht separat einen StringCreator?

Wie Ark schon angedeutet hat geht das in Richtung funktionaler Programmierung (das driftet dann ggf. etwas vom Thema ab, wenn es ausartet kann man es ja abtrennen (und vielleicht mit einem der vielen anderen Threads mergen, in denen genau darüber geredet wurde)).

Aber ich finde das durchaus etwas, was öfter mal "fehlt". Im Moment gibt es sowas wie
[c]someList.contains(element);[/c]
was die Liste durchgeht und überprüft, ob es ein Element gibt, das equals(element) ist. Man muss sich nur vorstellen, wie die Mächtigkeit dieser Funktion explodieren würde, wenn man dort ein "Predicate" übergeben könnte:
Java:
someList.contains(Predicates.equalsTo(element));
someList.contains(Predicates.hasNameLike(somePerson)); // mit Person#getName()
...
Die Vorteile, die so ein Konstrukt hätte, erschließen sich unmittelbar, wenn man einen Blick auf funktionale Programmierung wirft (vgl. auch "Interne Iteration" und "Externe Iteration", wie sie auch hier erwähnt ist: Java Collections API Design FAQ ).

Man könnte jetzt sagen: "Jaja, Gelaber, Java IST nunmal keine funktionale Sprache". Aber es wird wirklich immer wichtiger, fast unumgänglich - und das wurde erfreulicherweise auch erkannt und antizipiert: State of the Collections
 

Neue Themen


Zurück
Oben