toString() für equals-Vergleiche?

Einige kreative Programmierer nutzen die toString()-Repräsentation für Objektvergleiche. Etwas der Richtung: Wenn wir zwei Point-Objekte p und q haben, und p.toString().equals(q.toString()) ist, dann sind beide Punkte eben gleich. Doch ist es hochgradig gefährlich sich auf die Rückgabe von toString() zu verlassen aus mehreren Gründen: Offensichtlich ist, dass toString() nicht unbedingt überschrieben sein muss. Zweitens muss toString() nicht unbedingt alle Elemente repräsentieren und die Ausgabe könnte abgekürzt sein. Drittens können natürlich Objekte equals-gleich sein, auch wenn ihre String-Repräsentation nicht gleich ist, was etwa bei URL-Objekten der Fall ist. Der einzige erlaubte Fall für so eine Konstruktion wäre String/StringBuilder/StringBuffer/CharSequence, wo es ausdrücklich um Zeichenketten geht.

Habe ich Gründe vergessen?

Über Christian Ullenboom

Ich bin Christian Ullenboom und Autor der Bücher ›Java ist auch eine Insel. Einführung, Ausbildung, Praxis‹ und ›Java SE 8 Standard-Bibliothek. Das Handbuch für Java-Entwickler‹. Seit 1997 berate ich Unternehmen im Einsatz von Java. Sun ernannte mich 2005 zum ›Java-Champion‹.

2 Gedanken zu „toString() für equals-Vergleiche?

  1. Ja: die unter Umständen _schreckliche_ Performance. Eine gute equals Methode überprüft ja as Erstes die Klasse des anderen Objekts auf Gleichheit und danach im Idealfall zuerst Felder, die sich am wahrscheinlichsten unterscheiden und gleichzeitig billig zu vergleich sind (vgl. Effective Java) – und bricht den Vergleich sofort ab, sobald ein Unterschied gefunden wurde.

    Wird jedoch die String-Repräsentation eines Objekts verwenden, so müssen immer erst beide Objekte in Strings konvertiert werden – auch wenn ein direkter Vergleich viel früher abbrechen würde. Das mag bei kleinen Objekten wie Point nicht viel ausmachen, aber bei großen Listen von Objekten, die ihrerseits wiederum komplexe Objekte enthalten, welche dann ebenfalls in Strings konvertiert werden müssen… Ganz davon abgesehen dass diese derart umständlich erzeugten Strings im allgemeinen nicht wiederverwendet werden, sondern nach dem Vergleich sofort wieder vom GC abgearbeitet werden müssen.

    Um auch hier beim Beispiel der großen Liste wieder auf das Argument der billigen Vergleichbarkeit zurückzukommen: Listen vergleichen als Erstes die Anzahl der Elemente, weil dieser Vergleich extrem billig ist und sich verschiedene Listen hier sehr wahrscheinlich unterscheiden. Verwendet man dagegen die String-Vergleich-Variante, so müssen für beide Listen erst die String-Repräsentationen erstellt werden, auch wenn die Listen unterschiedlich viele Elemente haben, nur um dann mühsam aufgrund eines Stringvergleichs über unter Umständen tausende von Zeichen hinweg festzustellen, dass die Listen nicht gleich sind und beide mühevoll konstruierten Strings wieder verworfen werden können.

    Weil wir gerade bei Collections sind ein weiterer Punkt: Nicht alle Collections haben eine definierte Reihenfolge. (Hash)Set und -Map sind Beispiele dafür und können die Reihenfolge ihrer Elemente im Laufe der Programmausführung auch ändern. Ich gehe mal davon aus, dass die Spezifikation flexibel genug ist und auch erlaubt, dass sich die Reihenfolge der Elemente _jederzeit_ ändern kann – auch zwischen zwei Aufrufen der toString Methode. Somit könnte es dann sein, dass
    mySet.equals(mySet)
    erwartungsgemäß true ergibt (apropos – hier wieder ein Vergleich, den eine gute equals-Methode ganz zu Beginn macht),
    mySet.toString().equals(mySet.toString())
    dagegen nicht. Im Prinzip ist das Beispiel hier zwar ähnlich wie das mit der URL, aber hier kann es dann unter Umständen sogar passieren, dass ein Objekt ungleich sich selbst ist.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.