Discussion:
Vergleichen von Objekten
(zu alt für eine Antwort)
Sascha Schäfer
2006-05-29 19:18:01 UTC
Permalink
Hallo,

Ich schreibe lieber jetzt mal einen neuen Beitrag, da ich mich das letzte
mal wohl nicht genau genug ausgedrückt habe. Trotzdem danke an diejenigen,
die mir helfen wollten.

Also mein Problem ist folgendermaßen. Ich möchte zwei Objekte vergleichen,
über die ich nichts weiß. Der Vergleich des Objektzustandes kann nur über
Reflection erfolgen. Oder hat hier jemand schon ein bessere Idee?
Bei dieses Objekten kann es sich aber auch um Collections oder Maps handeln.
Weiß jemand einen Algorithmus, mit den man Objektsammlung schnell und
effizient vergleichen kann. Zusätzlich muss ich erwähnen, dass ich von den
Elementen in den Objektsammlungen nicht erwarten kann, dass sie die
hashCode- bzw. die equals-Methode implementieren.



mfg
Sascha
Stefan Ram
2006-05-29 19:27:24 UTC
Permalink
Post by Sascha Schäfer
Also mein Problem ist folgendermaßen. Ich möchte zwei Objekte
vergleichen, über die ich nichts weiß. Der Vergleich des
Objektzustandes kann nur über Reflection erfolgen. Oder hat
hier jemand schon ein bessere Idee?
In Java implementiert jedes Objekt:

http://download.java.net/jdk6/docs/api/java/lang/Object.html#equals(java.lang.Object)

Also kannst Du das immer zum Vergleichen nehmen.
Post by Sascha Schäfer
Zusätzlich muss ich erwähnen, dass ich von den Elementen in den
Objektsammlungen nicht erwarten kann, dass sie die hashCode-
bzw. die equals-Methode implementieren.
Irgendwie müssen sie diese Methoden ja implementieren.

Wenn Du Objekte vergleichen willst, die diese Methoden
fehlerhaft oder mangelhaft definieren und Du über die Objekte
nichts weiter weißt, dann gibt es im allgemeinen keinen Weg.

Du könntest mit Reflection allerdings tatsächlich erkennen, ob
zwei Objekte die gleiche Klasse haben und alle Felder gleiche
Werte oder Referenzen auf gleichwertige Objekte. Dieser
Gleichheitsbegriff ist aber bei manchen Objekten nicht
unbedingt sinnvoll.
Stefan Ram
2006-05-29 19:33:32 UTC
Permalink
Post by Stefan Ram
Werte oder Referenzen auf gleichwertige Objekte. Dieser
Hier muß man auch darauf achten, daß das Verfahren
bei direkten oder indirekten Selbstreferenzen eines
Objektes terminiert.
Stefan Ram
2006-05-29 23:02:13 UTC
Permalink
Post by Stefan Ram
Wenn Du Objekte vergleichen willst, die diese Methoden
fehlerhaft oder mangelhaft definieren und Du über die Objekte
nichts weiter weißt, dann gibt es im allgemeinen keinen Weg.
Wie hier schon von verschiedenen Personen geschrieben wurde,
ist es eben nicht selbstverständlich, was »Gleichheit« bedeutet.
Für ein Paar von Objekten muß dies im allgemeinen definiert
werden. Sei a.getClass() == A.class und b.getClass() == B.class.

a.equals( b ) == b.equals( a )

ist dabei in Java etwas schwierig zu realisieren, denn, da
ich als Autor einer neuen Klasse A alle anderen Klassen B
nicht kenne, kann ich schwer deren Verhalten b.equals( a )
imitieren. Denkbar wäre:

public boolean equals( final java.lang.Object other )
{ if( other instanceof this.getClass() )...
else return other.equals( this ); }

Aber, wenn das jeder macht, dann gibt es ein endloses Hin-
und-her-Delegieren.

Auch, ob man nicht oben schreiben sollte

if( other.getClass() == this.getClass() )

anstelle von "instanceof" ist schon umstritten.

Wann zwei Dinge "gleich" sind ist also eine Frage der
Definition. Das erinnert mich an ein altes Thema: Nämlich
den Unterschied zwischen "das gleiche" und "dasselbe" ...

»Von zwei Dingen zu sagen, sie seien identisch, ist ein
Unsinn, und von Einem zu sagen, es sei identisch mit sich
selbst, sagt gar nichts.«

Ludwig Wittgenstein, Tractatus Logico-Philosophicus (5.5303)

Wir haben aber in der Sprache das Phänomen, daß wir eine Sache
mit zwei verschiedenen Weisen bezeichnen können. Im Falle der
Venus kann man daher sagen:

Der Morgenstern ist der gleiche Planet wie der
Abendstern.

Die verschiedenen sprachlichen Bezeichnungen können verschiedene
Wege der Erkenntnis der Sache in der Erfahrung wiederspiegeln.

Oder zwei unterschiedlich erklärte (definierte) Begriffe könnten
in einem Modell dieselbe Entität bezeichnen, was bei der
Definition der Begriffe zunächst nicht offensichtlich sein muß.

Dann gibt es das Wort "gleich" als Kurzform von "gleichwertig"
"~". Eine Funktion induziert eine Äquivalenzrelation in ihrem
Definitionsbereich, vermittels x ~ x1 :<==> f(x)=f(x1).

Betrachtet man beispielsweise die Abbildung, die einem Term
seinen Wert zuordnet, so ist der Term "2+2" dem Term "2*2"
zwar nicht gleich, aber gleichwertig.

Nun sagt man aber zu "gleichwertig" auch manchmal kurz
"gleich", also "2+2 ist gleich 2*2". Will man dann wirklich
von Gleichheit der Terme sprechen, so verwendet man
"dasselbe". Also "'2+2' ist gleich '2*2', aber '2+2' ist nicht
dasselbe wie '2*2'."

Aber auch Terme können wieder hinsichtlich einer anderen
Äquivalenzrelation identifiziert werden, denn der Term "2+2"
ist dem Term "2 +2" gleich, obwohl die Zeichenfolgen
unterschiedlich sind.

Daher kann man, wenn man genau sprechen will, die
Unterscheidung zwischen "das gleiche" und "dasselbe" aufgeben
und statt dessen jeweils die Gattung und damit den Begriff
genau angeben, also:

- Der Wert "2+2" ist gleich dem Wert "2*2".
- Der Term "2+2" ist gleich dem Term "2+ 2".
- Die Zeichenfolge "2+2" ist gleich der Zeichenfolge
"2+2".

Der Unterschied zwischen "das gleiche" und "dasselbe" wird
gerne gemacht, wenn es im Kontext zwei verschiedene
Äquivalenzrelationen gibt, um dann mit "das gleiche" einen
bezug auf die gröbere und mit "dasselbe" einen Bezug auf die
feinere Äquivalenzrelation zu markieren.

Das typische Beispiel ist wohl

"Du hast dasselbe Kleid wie Deine Freundin an!"
"Nein - ich habe das gleiche Kleid wie sie an!"

Hier beizeichnet "das gleiche" für die als zweite sprechende
Person Gleichheit hinsichtlich der Äquivalenzrelation "vom
gleichen Produktionsmuster" (Schnitt, Art des Stoffes, Art des
Musters), "dasselbe" bezeichnet Identität einer physikalischen
Sache im Sinne ihres verfolgbaren Wegs in der Raumzeit.

Aber diese Zuordnung ist nicht zwingend, so daß eine
Verbesserung von jemandem, der "das gleiche" oder "dasselbe"
anders verwendet, als man dies selber tun würde, oft unnötig
und nicht immer zwingend ist. Schließlich kann man meist
verstehen, was gemeint ist. Auch kann der Sprecher mit einem
gewissen Recht "das gleiche" und "dasselbe" immer so
definieren, daß seine Verwendung richtig ist.

Aber auch die Identität eines einzelnen Kleides enthält nicht
selbstverständliche Selbstidentität, sondern eine
interpretierende Äquivalenzrelation. Ist der ehemalige
Bundeskanzler Gerhard Schröder noch derselbe Mensch, wie der
Juso-Vorsitzende Gerhard Schröder? Wie viele Atome, die damals
seinen Körper ausmachten, sind inzwischen durch Stoffwechsel
ausgetauscht worden?

Sind zwei Bosonen, die in allen Quantenzahlen übereinstimmen,
dieselbe Sache?

Also, man darf auf diesem Gebiete nichts für
selbstverständlich halten, sondern sollte die verwendeten
Begriffe und Äquivalenzrelationen möglichst explizit angeben
oder sich ihrer wenigsten bewußt sein.

Heraklit sagte:

"Niemand kann zweimal in den gleichen Fluß steigen, denn
immer andere Wasser strömen beständig nach."

Kratylos soll geschrieben haben:

"Niemand kann auch nur einmal in den gleichen Fluß steigen."

Und Wittgenstein wurde zugeschrieben:

"Der Mann, der sagte, man könne nicht zweimal in den
gleichen Fluß steigen, sagte etwas Falsches; man kann
zweimal in den gleichen Fluß steigen."

Diese Zitate zeigen auch einmal wieder, daß auf diesem Gebiet
keine Einigkeit über die Begriffe besteht.

Tatsächlich geht es aber darum, was man unter einem Fluß
überhaupt versteht, insbesondere wann zwei Beschreibungen von
Flüssen den gleiben Fluß beschreiben.

Einerseits versteht man unter einem Fluß eben das Wasser,
welches gerade in einem den Fluß identifizierenden Flußbette
ist. Andererseits wird der Fluß auch oft als durch sein Bett
identifiziert und nicht durch die als individuell markierbar
angenommenen Wasserteilchen darin.

Je nach der Definition von »Flußß ist es also egal, ob man
"den gleichen" oder "denselben" Fluß meint: Man /kann/ zweimal
in den gleichen (oder "denselben") Fluß steigen.

Manchmal wird behaupt: "Mutter und Tochter benutzen dasselbe
Parfum" bedeute immer »es gibt nur einen Flacon, den sich
Mutter und Tochter teilen«

Das ist aber die von mir nicht geschätzte starre
Interpretation, die ihre Subjektivität unbewußt, diese als
objektiv richtig vermeint.

Tatsächlich kann man aber genauso gut den Flakon als Klasse
ansehen ("zwei Parfümtropfen sind gleich, wenn sie aus
demselben Flakon kommen") und als individuell nur den gleichen
Tropfen ansehen.

Weil es eben subjektiv ist, was man als Klasse und was als
Individuum ansieht, wie ich hoffe mit obigem Beispiel gezeigt
zu haben, sollte man es anderen zugestehen, ihre eigene
Auffassung davon zu haben. Deswegen würde ich niemanden
korrigieren, der "dasselbe" oder "das gleiche" irgendwie
verwendet, solange ich verstehen kann, was er meint.

Viele mathematische Modelle enthalten heute ja nur noch
Mengen, und für die kann man Gleichheit dann stets extensional
definieren:

»x = y« bedeutet: »e ist Element von x« hat den Wahrheitswert
von »e ist Element von y« für alle e

In der Physik hat man Systeme und Zustände und muß so beim
Vergleich sagen, ob man fragt, ob dasselbe System oder
dasselbe System und derselbe Zustand vorliegt.
Ralf Ullrich
2006-05-29 23:53:27 UTC
Permalink
Post by Stefan Ram
Manchmal wird behaupt: "Mutter und Tochter benutzen dasselbe
Parfum" bedeute immer »es gibt nur einen Flacon, den sich
Mutter und Tochter teilen«
Erstmal Kompliment, eine echt unterhaltsame Ausführung.

Zum zitierten Ausschnitt: Ich würde, selbst wenn Mutter und Tochter nicht
zusammen wohnen, in unterschiedlichen Drogerien Einkaufen und definitiv
noch nie den Flacon der anderen auch nur gesehen haben, davon sprechen,
dass sie dasselbe Parfum benutzen, wenn es sich nur um dieselbe Marke
handelt. Parfums sind schließlich Gattungsware und damit die Einzelstücke
(sofern sie sich nicht zum Beispiel in der Flacongröße unterscheiden)
prinzipiell ununterscheidbar. (OK, Chargen-Nummern, und ähnliches heben
die Ununterscheidbarkeit in der Praxis auch wieder auf, aber wenn ich
zuhause feststelle, dass der eben teuer erstandene Flacon durch einen
Fabrikationsfehler nur halb voll ist, bin ich mit einem Umtausch auch dann
einverstanden, wenn sich die Chargen-Nummer unterscheidet.)

Anders ausgedrückt: Zwei Personen könnten durchaus dasselbe Wasser
*)trinken, ohne dass es unappetitlich wird.

cu

*) Ich denke da an Evian, Volvic, Adelheidquelle, etc.
Sylvia Schuetze
2006-05-30 08:20:11 UTC
Permalink
Post by Ralf Ullrich
Post by Stefan Ram
Manchmal wird behaupt: "Mutter und Tochter benutzen dasselbe
Parfum" bedeute immer »es gibt nur einen Flacon, den sich
Mutter und Tochter teilen«
Erstmal Kompliment, eine echt unterhaltsame Ausführung.
Zum zitierten Ausschnitt: Ich würde, selbst wenn Mutter und Tochter
nicht zusammen wohnen, in unterschiedlichen Drogerien Einkaufen und
definitiv noch nie den Flacon der anderen auch nur gesehen haben, davon
sprechen, dass sie dasselbe Parfum benutzen, wenn es sich nur um
dieselbe Marke handelt.
Sie benutzen dann das gleiche Parfum.

Oder ist der von mir in der Schule (lange her) gelernte Unterschied von
dasselbe und das Gleiche verschwunden (schon die neumodische
Rechtschreibung lässt mich das fast vermuten). Die Regel lautete: Man
kann sagen: ein und dasselbe aber man kann nicht sagen ein und das
Gleiche, also gibt es einen Bedeutungsunterschied. Den scheint aber
heute kaum noch jemand zu kennen.

Gruß Sylvia.
Ralf Ullrich
2006-05-30 08:50:43 UTC
Permalink
Post by Sylvia Schuetze
Post by Ralf Ullrich
Post by Stefan Ram
Manchmal wird behaupt: "Mutter und Tochter benutzen dasselbe
Parfum" bedeute immer »es gibt nur einen Flacon, den sich
Mutter und Tochter teilen«
Erstmal Kompliment, eine echt unterhaltsame Ausführung.
Zum zitierten Ausschnitt: Ich würde, selbst wenn Mutter und Tochter
nicht zusammen wohnen, in unterschiedlichen Drogerien Einkaufen und
definitiv noch nie den Flacon der anderen auch nur gesehen haben, davon
sprechen, dass sie dasselbe Parfum benutzen, wenn es sich nur um
dieselbe Marke handelt.
Sie benutzen dann das gleiche Parfum.
Oder ist der von mir in der Schule (lange her) gelernte Unterschied von
dasselbe und das Gleiche verschwunden (schon die neumodische
Rechtschreibung lässt mich das fast vermuten). Die Regel lautete: Man
kann sagen: ein und dasselbe aber man kann nicht sagen ein und das
Gleiche, also gibt es einen Bedeutungsunterschied. Den scheint aber
heute kaum noch jemand zu kennen.
Den Bedeutungsunterschied gibt es schon noch, nur: Für dich scheint die
Identität für ein Parfum eine materielle Bindung an die Substanz im Flacon
zu haben, wenn für dich beide im beschriebenen Fall nur "das gleiche"
Parfum benutzen. Für mich ist aber die Identität für ein Parfum an seine
Essenz, sprich an seine Komposition, seine materielle Zusammensetzung,
gebunden. Daher benutzen sie für mich "dasselbe" Parfum, auch wenn sie
nicht dieselben Aroma-Moleküle auf ihre Haut aufbringen können.

Falls es dich tröstet: Wie Stefan schon schrieb, haben wir beide recht.
Ich finde nur meine Sicht logischer, und du offensichtlich deine.

cu
Sylvia Schuetze
2006-05-30 08:53:03 UTC
Permalink
Post by Ralf Ullrich
Den Bedeutungsunterschied gibt es schon noch, nur: Für dich scheint die
Identität für ein Parfum eine materielle Bindung an die Substanz im
Flacon zu haben, wenn für dich beide im beschriebenen Fall nur "das
gleiche" Parfum benutzen. Für mich ist aber die Identität für ein Parfum
an seine Essenz, sprich an seine Komposition, seine materielle
Zusammensetzung, gebunden. Daher benutzen sie für mich "dasselbe"
Parfum, auch wenn sie nicht dieselben Aroma-Moleküle auf ihre Haut
aufbringen können.
OK, dann meinen wir auf unseren verschiedenen Makroebenen inhaltlich
dasselbe :-) und lassen mal die Moleküle außen vor.

Stefans Ausführungen sind sehr interessant, mir fällt nur immer wieder
auf, dass kaum noch jemand die unterschiedlichen Bedeutungen kennt,
obwohl es mal Lehrstoff der 3. oder 4. Klasse war...

Gruß Sylvia.
Marco Schmidt
2006-05-29 19:40:56 UTC
Permalink
Sascha Schäfer:

[...]
Post by Sascha Schäfer
Also mein Problem ist folgendermaßen. Ich möchte zwei Objekte vergleichen,
über die ich nichts weiß.
Wie soll das denn funktionieren? String und Integer haben
sinnvollerweise eigene Vorstellungen davon, wie die Ordnung über ihren
jeweils möglichen Werten definiert ist. Beliebige Objekte miteinander
zu vergleichen kann also gar nicht funktionieren.

Du müßtest also schon irgendwas über Deine Objekte wissen, um dann
#equals / Comparable / Comparator / Collator / Collections.sort
erfolgreich einsetzen zu können.

[...]

Gruß,
Marco
--
Bitte nur in der Newsgroup antworten, nicht per Email!
Meine Java-Seiten: http://schmidt.devlib.org/java/
FAQ: http://www.faqs.org/faqs/de/comp-lang-java/faq/
de.comp.lang.java Homepage: http://www.dclj.de/
Joerg Simon
2006-05-29 19:58:13 UTC
Permalink
Post by Marco Schmidt
[...]
Post by Sascha Schäfer
Also mein Problem ist folgendermaßen. Ich möchte zwei Objekte vergleichen,
über die ich nichts weiß.
Wie soll das denn funktionieren? String und Integer haben
sinnvollerweise eigene Vorstellungen davon, wie die Ordnung über ihren
jeweils möglichen Werten definiert ist. Beliebige Objekte miteinander
zu vergleichen kann also gar nicht funktionieren.
Du müßtest also schon irgendwas über Deine Objekte wissen, um dann
#equals / Comparable / Comparator / Collator / Collections.sort
erfolgreich einsetzen zu können.
Also ohne jetzt zu sehr ins detail gehen zu wollen, weil ich eigentlich
keine zeit habe, und nur zur ablenkung die newsgroup lese:

Also, es gibt sinnvolle vergleiche für alle primitiven daten-typen. Wenn
du dir die meisten hashCode implementierungen ansiehst dann summieren
die einfach werte für ihre primitiven daten typen auf, und addieren die
hashCode werte für alle complexen datentypen dazu.

Mann kann mit refl. schon querien ob es sich um einen primitiven
datentyp handelt (ich weiß jetzt nicht genau wie das hieß, aber ich
glaube es war sowas weltbewegendes wie isInt usw.).
D.h. man kann eimal auf basis dieser infos vergleichen (und mit refl.
kann man sich die priv. members auch abtasten...), und durch die obj.
durchgeben.

Also ich sehe das schon als möglich...
Post by Marco Schmidt
[...]
Gruß,
Marco
Marco Schmidt
2006-05-29 23:14:04 UTC
Permalink
Joerg Simon:

[...]
Post by Joerg Simon
Also ich sehe das schon als möglich...
Man kann den Typ zweier Objekte ermitteln und feststellen, ob er für
beide gleich ist. Dann könnte man equals aufrufen, oder compareTo, je
nachdem, was man vorgefunden hat.

Nur: Warum möchte man zwei beliebige Objekte vergleichen, über die man
sonst nichts weiß, nicht mal, ob sie typgleich sind? Denn das stand im
OP. Ich würde gern mehr über die Umstände erfahren. Vielleicht kann
man einen besseren Ansatz empfehlen. Wann immer Reflection verwendet
wird, werde ich besonders vorsichtig. Das hat auch seine berechtigten
Einsatzgebiete, aber das sind nicht sehr viele.

Gruß,
Marco
--
Bitte nur in der Newsgroup antworten, nicht per Email!
Meine Java-Seiten: http://schmidt.devlib.org/java/
FAQ: http://www.faqs.org/faqs/de/comp-lang-java/faq/
de.comp.lang.java Homepage: http://www.dclj.de/
Joerg Simon
2006-05-29 20:01:54 UTC
Permalink
Post by Sascha Schäfer
Hallo,
Ich schreibe lieber jetzt mal einen neuen Beitrag, da ich mich das letzte
mal wohl nicht genau genug ausgedrückt habe. Trotzdem danke an diejenigen,
die mir helfen wollten.
Also mein Problem ist folgendermaßen. Ich möchte zwei Objekte vergleichen,
über die ich nichts weiß. Der Vergleich des Objektzustandes kann nur über
Reflection erfolgen. Oder hat hier jemand schon ein bessere Idee?
Bei dieses Objekten kann es sich aber auch um Collections oder Maps handeln.
Weiß jemand einen Algorithmus, mit den man Objektsammlung schnell und
effizient vergleichen kann. Zusätzlich muss ich erwähnen, dass ich von den
Elementen in den Objektsammlungen nicht erwarten kann, dass sie die
hashCode- bzw. die equals-Methode implementieren.
mfg
Sascha
Hm, wie wäre es mit folgender Idee:
so ziemlich jede collection in java untersützt die getEnumerator methode
(naja, ich hoffe ich habe jetzt den java, und nicht den c# namen genannt
[ich verwechsle das graffel immer @_@]).

Du könntest also reflekitv querien ob ein objekt die methode hat, und
dann einen enum herausholen, und damit durch die col. iterieren. Dann
vergleichst du jedes obj. usw.

Ich habe gerade keine Zeit es herraus zu suchen, aber im Buch "Java
Reflections" wird unter anderem ein Objekt-Serialisierer implementiert.
Mit ein wenig anpassung sollte das mit dem gehen!

Lg,
Jörg Simon
Ralf Ullrich
2006-05-29 20:26:41 UTC
Permalink
Post by Sascha Schäfer
Hallo,
Ich schreibe lieber jetzt mal einen neuen Beitrag, da ich mich das letzte
mal wohl nicht genau genug ausgedrückt habe. Trotzdem danke an diejenigen,
die mir helfen wollten.
Hmmm. Ich habe nicht den Eindruck, dass es dir diesmal besser gelunden ist
dich auszudrücken. ;-) SCNR
Post by Sascha Schäfer
Also mein Problem ist folgendermaßen. Ich möchte zwei Objekte vergleichen,
über die ich nichts weiß. Der Vergleich des Objektzustandes kann nur über
Reflection erfolgen. Oder hat hier jemand schon ein bessere Idee?
Ja. Vergiß Reflection, nimm einfach equals(). Wie Stefan schon schrieb,
equals() ist immer definiert.
Post by Sascha Schäfer
Bei dieses Objekten kann es sich aber auch um Collections oder Maps handeln.
Weiß jemand einen Algorithmus, mit den man Objektsammlung schnell und
effizient vergleichen kann. Zusätzlich muss ich erwähnen, dass ich von den
Elementen in den Objektsammlungen nicht erwarten kann, dass sie die
hashCode- bzw. die equals-Methode implementieren.
Brauchen sie ja auch nicht, hashCode und equals gibt es, wie gesagt bei
jedem Objekt. Wenn eine Klasse equals() nicht überschreibt, dann hat das
in der Regel gute Gründe. Beispiel:

public class Counter {
private int v = 0;

public int next() { return ++v; }
public void reset() { v = 0; }
}

Wenn du nun zwei Counter Objekte hast

Counter a = new Counter();
Counter b = new Counter();

was soll dann ein Vergleich dieser beiden Objekte für ein Ergebnis bringen?

Ich meine es sind zwei völlig verschiedene Counter. Nur weil ein zudem
sehr veränderliches Attribut zufällg gerade in diesem Moment denselben
Wert aufweist, sind die Counter-Objekte ja noch lange nicht gleich. Sprich
sie haben zwar momentan den gleichen internen Zustand, sind deswegen aber
noch lange nicht gleich.

Die Default-Implementation von equals spiegelt diese Eigenschaft der
Counter genau wieder, deshalb braucht sie auch nicht überschrieben werden.



Was ich sagen will: Wenn du nichts über die Objekte weißt, ist das völlig
in Ordnung. Eigentlich ist es sogar ein Grundprinzip bei OO, dass man nur
so wenig über ein Objekt wissen soll, wie gerade nötig. Im Gegensatz dazu,
sollten aber die Objekte selbst alles über sich wissen.

Was das Vergleichen angeht, bedeutet das: Alles was du wissen musst, ist,
dass jedes Objekt eine equals() Methode hat, die du aufrufen kannst. Das
jeweilige Objekt muss dafür sorgen dass diese das richtige macht, nicht du
von außen!

Wenn du mit vergleichen meinst, schauen ob ein Objekt "größer" als ein
anderes ist, dann ist zunächst mal wichtig zu erkennen, dass man nicht
beliebige Sachen in ihrer Größe vergleichen kann. Oder was ist größer: 1
Kilometer oder Zwei Stunden?

D.h. für diese Art Vergleich brauchst du nur wissen, dass du entweder
einen geeigneter Vergleicher (Comparator) hast, der den Vergleich für dich
vornimmt, oder dass das Objekt selbst annonciert, dass es verglichen
werden kann (Comparable).


Nochmal, weil ich es (besonders nach dem Post von Joerg Simon) wohl nicht
deutlich genug sagen kann: Vergiß es mit Reflection eine allg.
Vergleichs-Lösung bauen zu wollen. Alles was du damit fabrizierst ist
Unsinn.

Wenn du jetzt immer noch meinst du brauchst so etwas, dann beschreibe doch
lieber dein zugrundeliegendes Problem. Da können wir dir sicher besser bei
helfen, als bei so einem unsinnigen Vorhaben wie einen
Reflection-basierten Vergleicher-Algorithmus zu schreiben.

cu
Sascha Schäfer
2006-05-29 21:05:30 UTC
Permalink
Also noch mal zur Ergänzung meiner Problemstellung.

Ich möchte zwei Objekte vergleichen, die aus zwei verschiedenen
Objektmodellen kommen. Die Klassen stimmen überein, die Referenz aber nicht.
Somit fällt die standardmäßige equals-Methode zum Vergleichen schon mal weg.
Um zwei Objekte zu vergleichen, muss ich deren Zustände untersuchen. Das
geht nur, indem ich die Attributwerte betrachte. Und das wiederum geht nur
über Reflection, weil ich nichts über das Objekt selbst weiß. Verstanden?


mfg
Sascha
Ralf Ullrich
2006-05-29 21:28:52 UTC
Permalink
Post by Sascha Schäfer
Also noch mal zur Ergänzung meiner Problemstellung.
Ich möchte zwei Objekte vergleichen, die aus zwei verschiedenen
Objektmodellen kommen. Die Klassen stimmen überein, die Referenz aber nicht.
Somit fällt die standardmäßige equals-Methode zum Vergleichen schon mal weg.
Um zwei Objekte zu vergleichen, muss ich deren Zustände untersuchen. Das
geht nur, indem ich die Attributwerte betrachte. Und das wiederum geht nur
über Reflection, weil ich nichts über das Objekt selbst weiß. Verstanden?
Ne ich hab's nicht verstanden. Für mich sind die Objekte so gesehen
schlicht verschieden, wenn sie nicht dieselbe Referenz haben, und das
macht Object#equals(). Punkt.


Hast du's nicht genauer? Beispiele? Wieso kommen die aus zwei
verschiedenen Objektmodellen und haben doch die gleiche Klasse? Haben zwei
Objektmodelle nicht normalerweise ihre eigenen (und damit verschiedenen)
Klassen? Wenn die Klassen gleich sind, warum sind es dann verschiedene
Objektmodelle? Wenn du nichts über die Objekte weißt, wie willst du dann
die Attributwerte, nachdem du sie mit Reflection entrissen hast, überhaupt
vergleichen?

Du schreibst: "Um zwei Objekte zu vergleichen, muss ich deren Zustände
untersuchen." Musst du wirklich? Woher kommt der Zwang dieses zu müssen?
Kannst du das erklären?


Das ich das was du vorhast für Unsinn halte habe ich ja schon gesagt. Mit
der Frage nach dem "muss" möchte ich dich nicht foppen, sondern
herausfinden was du denkst. Ich glaube nämlich dein Irrtum liegt genau in
diesem "muss". Wenn du mir erklären kannst, warum du musst, und zwar so,
dass ich es verstehe, dann kann ich dir vielleicht erklären, warum du eben
doch nicht musst, oder welchen Weg du dafür einschlagen kannst.

Aber ich könnte verstehen, wenn dir die weitere Diskussion mit mir
fruchtlos erscheint.


Vielleicht noch ein Satz: Du hast geschrieben, du hast über dieses Thema
fast gar nichts gefunden. Frage: Besteht denn nicht die Möglichkeit, dass
du deshalb nichts darüber im Netz findest, weil es schlicht und
ergreiflich, zwar (über Reflection) theoretisch möglich, aber praktisch
(völlig) unsinnig ist, und es deshalb (außer dir) niemand ernsthaft in
Betracht zieht, und folglich auch niemand etwas darüber ins Netz stellt?
Nur mal so ein Gedanke.

cu
Sascha Schäfer
2006-05-30 12:22:02 UTC
Permalink
Ich hab mich wohl etwas falsch ausgedrückt. Ich entschuldige mich dafür. Das
Objektmodell ist auch dasselbe. Ich habe nur zwei verschieden Zustände des
Objektmodells gespeichert. Zum Beispiel speichert das Objektmodell
Personaldaten ab. Jemand ändert die Personaldaten einer Person (ändert
Gehalt, Arbeitsstunden...) und speichert dies ab. Danach soll der Vergleich
der beiden Zustände die Änderungen sichtbar machen. Der vergleich soll aber
nicht nur auf das Objektmodell beschränkt sein, sondern Zustände jedes
Objektmodells vbergleichen können.
Deswegen möchte ich Reflection verwenden. Ich merke, dass mir alle davon
abraten wollen. Aber konkrete Gründe hat mir keiner genannt. Die würden mich
nämlich auch mal interessieren.


mfg
Sascha
Jochen Theodorou
2006-05-30 13:20:55 UTC
Permalink
Post by Sascha Schäfer
Ich hab mich wohl etwas falsch ausgedrückt. Ich entschuldige mich dafür. Das
Objektmodell ist auch dasselbe. Ich habe nur zwei verschieden Zustände des
Objektmodells gespeichert. Zum Beispiel speichert das Objektmodell
Personaldaten ab. Jemand ändert die Personaldaten einer Person (ändert
Gehalt, Arbeitsstunden...) und speichert dies ab. Danach soll der Vergleich
der beiden Zustände die Änderungen sichtbar machen. Der vergleich soll aber
nicht nur auf das Objektmodell beschränkt sein, sondern Zustände jedes
Objektmodells vbergleichen können.
also es reicht dir nciht zu wissen das etwas geändert wurde, sondern du
musst wissen was geändert wurde. Richtig?
Post by Sascha Schäfer
Deswegen möchte ich Reflection verwenden. Ich merke, dass mir alle davon
abraten wollen. Aber konkrete Gründe hat mir keiner genannt. Die würden mich
nämlich auch mal interessieren.
Weil Reflection nicht immer so eifnach ist wie manche sich das denken.
In deinem Beispiel brauchst du ja den gesamten Objektzustand. Das heisst
auch Werte privater Felder und vorallem privater Felder in
Elternklassen. Dabei muss man bedenken dass die dann gleich heissen dürfen

class A {
private int foo = 1
}

class B extends A {
private int foo = 2
}

du hast das Feld foo also 2x mit unterschiedlichen Werten.

im Prinzip hast du dann sowas

void compareObject(Object object1, Object object2)(
Class cls = object1.getClass()
if (cls != objects.getClass()) reportNotEqual();
while (cls!=null) {
Field[] fileds = cls.getDeclaredFields()
for (Field f : fields) compareObject(f,object1,object2)
cks=cls.getSuperClass()
}
}

natürlich nicht das setAcessible vergessen, ausserdem aufpassen bei null
und du musst dafür sorgen, dass du nicht in eine Endlosschleife gerätst,
weil irgendwo ein Ring besteht. Die rekursive From ist eh nicht so toll,
denn der Pfad im Objektbaum könnte länger sein, als deine maximale
Rekursionstiefe... zum Beispiel bei einer grossen verketteten Liste. Der
nächste Nachteil ist, dass es im Vergleich zum normalen Feldzugriff
nicht besonders schnell ist.

Ausserdem kann es hier durchaus passieren dass equals true liefern
würde, der Vergleich aber nicht... z.B. weil beim equal bewusst ein Feld
nicht verglichen wird.

Und ncoh ein Problem, Threadsicherheit!

Gruss theo
Sascha Schäfer
2006-05-30 16:02:08 UTC
Permalink
Du hast mich richtig verstanden und deine Argumente sind auch sehr
einleuchtend. Aber gibt es denn in meinem Problem eine Alternative zu
Reflection?


mfg
Sascha
Jochen Theodorou
2006-05-30 18:49:43 UTC
Permalink
Post by Sascha Schäfer
Du hast mich richtig verstanden und deine Argumente sind auch sehr
einleuchtend. Aber gibt es denn in meinem Problem eine Alternative zu
Reflection?
wenn du Einfluss auf die Klassen nehmen kannst, dann nimm Ralfs Ansatz.
Ansonsten bleibt wirklich nur Reflection mit den angesprochenen Vor- und
Nachteilen. Zumindest fällt mir ncihts besseres ein.

Gruss theo
Ralf Ullrich
2006-05-30 18:03:52 UTC
Permalink
Post by Sascha Schäfer
Ich hab mich wohl etwas falsch ausgedrückt. Ich entschuldige mich dafür. Das
Objektmodell ist auch dasselbe. Ich habe nur zwei verschieden Zustände des
Objektmodells gespeichert. Zum Beispiel speichert das Objektmodell
Personaldaten ab. Jemand ändert die Personaldaten einer Person (ändert
Gehalt, Arbeitsstunden...) und speichert dies ab. Danach soll der Vergleich
der beiden Zustände die Änderungen sichtbar machen. Der vergleich soll aber
nicht nur auf das Objektmodell beschränkt sein, sondern Zustände jedes
Objektmodells vbergleichen können.
Deswegen möchte ich Reflection verwenden. Ich merke, dass mir alle davon
abraten wollen. Aber konkrete Gründe hat mir keiner genannt. Die würden mich
nämlich auch mal interessieren.
OK, jetzt verstehe ich was du erreichen willst. Zunächst jedoch zu den
konkreten Gründen gegen Reflection: technisch gibt es keine, aber in der
Art wie du sie für diese Aufgabe einsetzen willst, bricht Reflection halt
sozusagen mit allen Regeln guten Geschmacks bzw. guten OO-Designs.
Konkreter zu bergründen geht schlecht.


Zunächst mal würde ich versuchen das ganze in ein OO-Design zu packen. Das
fängt dann damit an, dass du bestimmen musst, wie die Unterschiede
festgehalten werden sollen. Da beispielsweise bei

class A {
int x;
}
class B extends A {
int x;
}

es zwei verschiedene Attribute mit dem Namen "x" gibt, müsste man wohl
neben dem reinen Attributnamen, auch den Namen der Klasse, in der es
definiert wurde dazunehmen. Eine erste Definition eines Unterschieds
könnte damit so aussehen (ich wähle hier mal deutsche Bezeichner):

class Unterschied {
Class definierendeKlasse;
String attributName;
Object linkerWert;
Object rechterWert;
}

Da mehr als ein Attribut zwischen zwei Objekten verschieden sein kann,
ergibt sich der Gesamtunterschied zwischen zwei Objekten aus dem Set der
Einzelunterschiede. Nennen wir den Gesamtunterschied mal Differenz:

class Differenz {
Object linkesObjekt;
Objekt rechtesObjekt;
Set<Unterschied> unterschiede;
}

Wir können nun also mit Objekten der Klasse Differenz den
Gesamtunterschied zwischen linkem Objekt und rechtem Objekt darstellen,
also quasi "diff A B".


Jetzt brauchen wir noch einen Algorithmus um die Differenzbildung zu
ermöglichen. Hier würde ich drei Fälle unterscheiden:

1. A und B wissen wie sie die Differenz zueinander erstellen, dann frage
ich sie einfach danach

2. Ich kenne ein Objekt, dass eine Differenz zwischen A und B bilden kann,
dann hole ich mir so ein Objekt und bitte es die Differenz zu erstellen

3. Weder wissen A und B um ihre Differenz, noch gibt es ein Objekt, das
wüsste, wie die Differenz zu bilden ist. Hier kann ich mich entweder
entscheiden, zu sagen: Hey, was solls es gibt eine Differenz, aber ich
kann dir nicht sagen, wie sie aussieht. Oder alternativ: Es gibt eine
Differenz, und ich habe mal mit Reflection diese Unterschiede
festgestellt, aber das nun wirklich eine vernünftiger Differenz ist, kann
ich dir auch nicht sagen.

Nun im ersten Fall würde ich sagen sollten A und B ein Interface
implementieren:

interface Vergleichbar {
Differenz vergleicheMit(Objekt rechtesObjekt);
}

// code im algorithmus:
Differenz diffAB = ((Vergleichbar)a).vergleicheMit((Vergleichbar)b);

Im zweiten Fall sollte meinem Vergleichsalgorithmus eine Factory bekannt
sein, bei der er geeignete Vergleicher anfordern kann:

interface Vergleicher {
Differenz vergleiche(Objekt linkesObjekt, Objekt rechtesObjekt);
}

interface VergleicherFactory {
Vergleicher erzeugeVergleicher(Class linkeKlasse, Class rechte Klasse);
}

// code im algorithmus:
VergleicherFactory fac = ...
Vergleicher verglAB = fac.erzeugeVergleicher(a.getClass(), b.getClass());
Differenz diffAB = verglAB.vergleiche(a,b);

Erst im dritten Fall würde ich auf eine Reflection-Lösung ausweichen. Und
auch dort nur um Attribute, die den Beans-Contract erfüllen zu
vergleichen. D.h. wenn es sowohl get<AttributName>, als auch
set<AttributName> gibt, würde ich dieses Attribut vergleichen. D.h.
insbesondere würde ich nicht versuchen mit Reflection (via setAccessible)
private field member auszulesen.


Ich hoffe du kannst mit diesen Ausführungen schon was anfangen, wenn
nicht, frage gerne weiter nach. (Ich habe hier jetzt zum Beispiel noch
ausgelassen wie man mit Hierarchien umgehen könnte, aber es ist auch jetzt
schon viel, mit dem du wenigstens mal gedanklich experimentieren könntest.)

cu
Sascha Schäfer
2006-05-30 21:55:17 UTC
Permalink
Danke für die super Erklärung. Ich hoffe,das klingt nicht ironisch, denn ich
meine es wirklich ernst. Dein Ansatz hat mir weitergeholfen.


mfg
Sascha
b***@yahoo.de
2006-06-07 01:59:57 UTC
Permalink
zuerst, wenn es verschieden klassen sind (typ), unterscheiden sie sich
durch bestimmte properties oder methoden, ich wuerde die zu
vergeleichenden elemente( das musst du schon wissen bei jede klasse,
z.B. gehaelter, sex) in eine liste packen, und alle klassen, die du
vergleichen willst, eine interface implementieren, Vector
getCompareblePart(), und
object,getklasse,executemethode(getcomparablepart).equals vergleichen,
dann kannst du auch einen angestellten mit einem student z.B. das alter
und geschlecht vergleichen.
Andree Yang

Joerg Simon
2006-05-29 21:29:46 UTC
Permalink
Post by Sascha Schäfer
Also noch mal zur Ergänzung meiner Problemstellung.
Ich möchte zwei Objekte vergleichen, die aus zwei verschiedenen
Objektmodellen kommen. Die Klassen stimmen überein, die Referenz aber nicht.
Somit fällt die standardmäßige equals-Methode zum Vergleichen schon mal weg.
Um zwei Objekte zu vergleichen, muss ich deren Zustände untersuchen. Das
geht nur, indem ich die Attributwerte betrachte. Und das wiederum geht nur
über Reflection, weil ich nichts über das Objekt selbst weiß. Verstanden?
mfg
Sascha
Naja, dass ist aber wieder eine andere situation als die erste. D.h.
dass du schon so gewisse sachen weißt wie, dass z.B. die vergleiche der
primitiven zusände sinnvoll sind. Du willst also einen gewissen satz an
klassen vergleichen, bei denen du die implementierung kennst?

Lg,
Joerg
Timo Stamm
2006-05-29 21:30:54 UTC
Permalink
Post by Sascha Schäfer
Also noch mal zur Ergänzung meiner Problemstellung.
Ich möchte zwei Objekte vergleichen, die aus zwei verschiedenen
Objektmodellen kommen.
Das klingt nach keinem besonders ausgefeilten Plan. Welche Bedingungen
müssen denn Objekte erfüllen damit sie "gleich" sind?

Zum Beispiel folgende Situation:

class Train {
int speed = 10;
}

class Car {
int speed = 10;
}

Zwei Instanzen dieser Klassen könntest du Objekt für Objekt (bzw.
primitive) mit Reflection vergleichen, das Ergebnis
myCar.equals(myTrain) = true würde mich aber ziemlich irritieren.

Auch hier versagt Reflection (Beispiel ist nicht sinnvoll):

Picture {
private byte[] cache;
String url;
public Picture(String url) {
this.url = url;
}
getData() {
if (cache == null) {cache = ...}
return cache;
}
}

Wenn ich zwei Instanzen anlege, beide mit der selben URL, sind sie nicht
mehr "gleich" sobald eine Instanz ihre Daten gecached hat. Das würde
mich auch ziemlich irritieren.

Aus solchen Gründen ist ein Vergleich per Reflection nicht wirklich
sinnvoll.
Post by Sascha Schäfer
Die Klassen stimmen überein, die Referenz aber nicht.
Wenn es dieselben Klassen sind ist das Problem doch einfach zu lösen:

Picture {
...
boolean equals(Object o) {
return o instanceof Picture && ((Picture)o).url.equals(url);
}
}

Du musst entscheiden anhand welcher Eigenschaften sich Gleichheit festmacht.
Post by Sascha Schäfer
Somit fällt die standardmäßige equals-Methode zum Vergleichen schon mal weg.
Um zwei Objekte zu vergleichen, muss ich deren Zustände untersuchen. Das
geht nur, indem ich die Attributwerte betrachte. Und das wiederum geht nur
über Reflection, weil ich nichts über das Objekt selbst weiß. Verstanden?
Wenn du nichts über das Objekt weisst kannst du nur false zurückgeben.
Das Problem lässt sich nicht sinnvoll lösen, du steckst also in einer
Design-Sackgasse. Deshalb fragte dich Ralf danach was dein
zugrundeliegendes Problem ist. Also das Problem das du versuchst mit
einem Vergleich zwischen unbekannten Objekten zu lösen.


Timo
Joerg Simon
2006-05-29 21:31:20 UTC
Permalink
[snip]
Post by Ralf Ullrich
Brauchen sie ja auch nicht, hashCode und equals gibt es, wie gesagt bei
jedem Objekt. Wenn eine Klasse equals() nicht überschreibt, dann hat das
public class Counter {
private int v = 0;
public int next() { return ++v; }
public void reset() { v = 0; }
}
Wenn du nun zwei Counter Objekte hast
Counter a = new Counter();
Counter b = new Counter();
was soll dann ein Vergleich dieser beiden Objekte für ein Ergebnis bringen?
[snip]

Sehr gutes Beispiel. Da ist mein Post davor ja dann sowieso hinfällig
(naja, wie gesagt im stress geschrieben... ;) ).

Lg,
Joerg
Ralf Ullrich
2006-05-29 21:37:25 UTC
Permalink
Post by Joerg Simon
[snip]
Post by Ralf Ullrich
Brauchen sie ja auch nicht, hashCode und equals gibt es, wie gesagt bei
jedem Objekt. Wenn eine Klasse equals() nicht überschreibt, dann hat das
public class Counter {
private int v = 0;
public int next() { return ++v; }
public void reset() { v = 0; }
}
Wenn du nun zwei Counter Objekte hast
Counter a = new Counter();
Counter b = new Counter();
was soll dann ein Vergleich dieser beiden Objekte für ein Ergebnis bringen?
[snip]
Sehr gutes Beispiel. Da ist mein Post davor ja dann sowieso hinfällig
(naja, wie gesagt im stress geschrieben... ;) ).
Mir gefällt eigentlich Timo's Picture Beispiel mit Cache viel besser. Ich
habe bei meinem Post verzweifelt versucht ein Beispiel zu konstruieren,
bei dem die Attribute verschieden, das Objekt aber trotzdem gleich wäre.
Mir ist nur nix eingefallen.

Trotzdem, danke für die Blumen.

cu
Ingo Menger
2006-05-30 10:38:05 UTC
Permalink
Post by Ralf Ullrich
Mir gefällt eigentlich Timo's Picture Beispiel mit Cache viel besser. Ich
habe bei meinem Post verzweifelt versucht ein Beispiel zu konstruieren,
bei dem die Attribute verschieden, das Objekt aber trotzdem gleich wäre.
Mir ist nur nix eingefallen.
Ach, das ist ja nun einfach:

class Summe<N> {
N a, b;
Summe(N a, N b) { this.a = a; this.b = b; }
N eval() { .... }
}

(new Summe<Integer>(5,3)).equals(new Summe<Integer>(2,6)) ???

oder gar

(new Summe<Integer>(5,3)).equals(new Summe<Double>(3.0,5.0)) ???
Jochen Theodorou
2006-05-30 10:49:09 UTC
Permalink
Post by Ingo Menger
Post by Ralf Ullrich
Mir gefällt eigentlich Timo's Picture Beispiel mit Cache viel besser. Ich
habe bei meinem Post verzweifelt versucht ein Beispiel zu konstruieren,
bei dem die Attribute verschieden, das Objekt aber trotzdem gleich wäre.
Mir ist nur nix eingefallen.
class Summe<N> {
N a, b;
Summe(N a, N b) { this.a = a; this.b = b; }
N eval() { .... }
}
(new Summe<Integer>(5,3)).equals(new Summe<Integer>(2,6)) ???
oder gar
(new Summe<Integer>(5,3)).equals(new Summe<Double>(3.0,5.0)) ???
es kann auch Situationen geben wo man haben will dass die BigDecimals
1.0 und 1.00 gleich sind ;)

Gruss theo
Ingo R. Homann
2006-05-30 15:10:56 UTC
Permalink
Hi,
Post by Jochen Theodorou
es kann auch Situationen geben wo man haben will dass die BigDecimals
1.0 und 1.00 gleich sind ;)
I*M*HO ist das sogar der Normalfall. Schade, dass sun das anders sieht.
Mal abgesehen davon, dass

b1.equals(b2)

schon nicht schön ist (im Vergleich zu

b1==b2

), finde ich, dass

b1.compareTo(b2)==0

dem Fass den Boden aushaut.

Ciao,
Ingo
Jochen Theodorou
2006-05-30 16:17:31 UTC
Permalink
Post by Ingo R. Homann
Hi,
Post by Jochen Theodorou
es kann auch Situationen geben wo man haben will dass die BigDecimals
1.0 und 1.00 gleich sind ;)
I*M*HO ist das sogar der Normalfall. Schade, dass sun das anders sieht.
Mal abgesehen davon, dass
b1.equals(b2)
schon nicht schön ist (im Vergleich zu
b1==b2
), finde ich, dass
b1.compareTo(b2)==0
dem Fass den Boden aushaut.
das Problem ist mir halt bei Groovy das erste mal so richtig bewusst
geworden. Weil man da sehr leicht mit BigDecimals arbeiten kann tut man
dashalt auch gerne mal... und dann will man vielleicht mal ein Set
erzeugen und wundert sich dass 1.00* mehrmals drin ist, oder will alle
1.0 aus einer Liste entfernen und sieht dass 1.00 noch drin ist, usw.
Das kann ganz schön nerven

Gruss theo
Ingo Menger
2006-05-31 08:37:03 UTC
Permalink
Post by Jochen Theodorou
Post by Ingo R. Homann
Hi,
Post by Jochen Theodorou
es kann auch Situationen geben wo man haben will dass die BigDecimals
1.0 und 1.00 gleich sind ;)
I*M*HO ist das sogar der Normalfall. Schade, dass sun das anders sieht.
Mal abgesehen davon, dass
b1.equals(b2)
schon nicht schön ist (im Vergleich zu
b1==b2
), finde ich, dass
b1.compareTo(b2)==0
dem Fass den Boden aushaut.
das Problem ist mir halt bei Groovy das erste mal so richtig bewusst
geworden. Weil man da sehr leicht mit BigDecimals arbeiten kann tut man
dashalt auch gerne mal... und dann will man vielleicht mal ein Set
erzeugen und wundert sich dass 1.00* mehrmals drin ist, oder will alle
1.0 aus einer Liste entfernen und sieht dass 1.00 noch drin ist, usw.
Das kann ganz schön nerven
So wie die Klasse definiert ist, ist sie m.E. in der Tat unbenutzbar.
Man kann eigentlich nur MyBigDecimal ableiten und equals()
überschreiben.
Paul Ebermann
2006-05-31 11:59:45 UTC
Permalink
[BigDecimal#equals() verhält sich gewöhnungsbedürftig]
So wie die Klasse definiert ist, ist sie m.E. in der Tat unbenutzbar.
Man kann eigentlich nur MyBigDecimal ableiten und equals()
überschreiben.
Da musst du aber aufpassen, dass dann wirklich
konsistent nur MyBigDecimal-Objekte verwendet
werden und keine "echten" BigDecimals. Sonst wird
das Verhalten von #equals nämlich nicht nur
inkonsistent zu #compareTo, sondern auch noch zu
seiner eigenen Spezifikation (Symmetrie!).

Besser könnte es sein, hier Delegation zu verwenden.


Ich hatte mir ja mal das Konzept einer externen
Äquivalenzrelation ausgedacht, mit eingebautem
passenden Hashcode:

---
/**
* Eine Äquivalenzrelation auf einem Typ E.
*/
interface EquivalenceRelation<E>
{

/**
* Entscheidet, ob zwei Objekte als äquivalent
* angesehen werden.
*
* Dabei müssen für alle e1, e2, e3 des Typs E folgende Bedingungen
* eingehalten werden:
*
* (Reflexivität)
* areEqual(e1, e1);
* (Symmetrie)
* areEqual(e1,e2) ===> areEqual(e2,e1)
* (Transitivität)
* areEqual(e1,e2) && areEqual(e2,e3)
* ===> areEqual(e1, e3)
* (Konsistenz)
* Solange keine "relevanten" Daten geändert werden,
* ergibt areEqual(e1,e2) das gleiche. (Was genau
* relevant ist, hängt von der Äquivalenzrelation ab.)
*/
public boolean areEqual(E e1, E e2);

}
---
interface HashableEquivalenceRelation<E>
extends EquivalenceRelation<E>
{
/**
* Berechnet einen Häcksel-Code des Objektes E.
* Diese Methode muss derart zu areEqual passen,
* dass für alle Objekte e1 und e2 des Typs E gilt:
*
* areEqual(e1, e2) ===> hashCode(e1) == hashCode(e2)
*
* Außerdem muss der Wert gleich bleiben, solange keine
* der für areEqual() relevanten Daten geändert werden.
*
* Für eine effiziente Verwendung in Hashmaps o.ä. sollten
* auch für nicht-äquivalente Objekte soweit möglich
* verschiedene hashCodes herauskommen.
*
* Wenn ein Objekt in einer Hashmap o.ä. als Schlüssel verwendet
* wird, dürfen in dieser Zeit keine "relevanten" Änderungen
* vorgenommen werden.
*/
public int hashCode(E e);
}
---
Das kann man dann für die Implementation von Hashmaps analog zu
java.util.HashMap verwenden.

Hier ein paar Standard-Implementationen von EquivalenceRelation:

---
class Utils
{

/**
* Die equals()-Äquivalenz.
*/
public static final HashableEquivalenceRelation<Object> DEFAULT_EQUIVALENCE =
new HashableEquivalenceRelation<Object>() {
public boolean areEqual(Object o1, Object o2)
{
return o1 == null ? o2 == null : o1.equals(o2);
}
public int hashCode(Object o)
{
return o == null ? 42 : o.hashCode();
}
};

/**
* Eine Ä-Relation, die nur identische Objekte als gleich ansieht.
* (= die feinste mögliche Ä-Relation.)
*/
public static final HashableEquivalenceRelation<Object> IDENTITY_EQUIVALENCE =
new HashableEquivalenceRelation<Object>() {
public boolean areEqual(Object o1, Object o2)
{
return o1 == o2;
}
public int hashCode(Object o)
{
return System.identityHashCode(o);
}
};

/**
* Eine Ä-Relation, die alle Objekte als gleich ansieht.
* (= die gröbste mögliche Ä-Relation.)
*/
public static final HashableEquivalenceRelation<Object> ALL_EQUIVALENCE =
new HashableEquivalenceRelation<Object>() {
public boolean areEqual(Object o1, Object o2)
{
return true;
}
public int hashCode(Object o)
{
return 17;
}
};

/**
* Eine Ä-Relation, die zwei Number-Objekte als gleich ansieht, wenn
* sie auf volle double-Werte gerundet das gleiche ergeben.
*/
public static final HashableEquivalenceRelation<Number> DOUBLE_EQUIVALENCE =
new HashableEquivalenceRelation<Number>() {
public boolean areEqual(Number o1, Number o2)
{
return o1.doubleValue().equals(o2.doubleValue());
}
public int hashCode(Number o)
{
return o1.doubleValue().hashCode();
}
};


/**
* Eine Ä-Relation, die zwei String-Objekte auch dann als gleich
* ansieht, wenn eines von hinten gelesen das gleiche ist wie das
* andere von vorne.
*/
public static final HashableEquivalenceRelation<String> REVERSE_EQUIVALENCE =
new HashableEquivalenceRelation<String>() {
public boolean areEqual(String s1, String s2)
{
// TODO: optimieren
return s1.equals(s2) ||
(s1.length() == s2.length() && s1.equals(Utils.reverse(s2)));
}
public int hashCode(String s)
{
return s.hashCode ^ Utils.reverse(s).hashCode();
}
};

/**
* Hilfsmethode zum Umdrehen von Strings.
*/
private static String reverse(String s)
{
// TODO: Caching?
StringBuilder sb = new StringBuilder(s.length());
for (int i = s.length(); i >= 0; i--)
{
sb.append(s.charAt(i));
}
return sb.toString();
}


/**
* Eine Ä-Relation, basierend auf einem Comparator.
*/
public static final class ComparatorEquivalenceRelation<E>
implements EquivalenceRelation<E>
{
private final Comparator<E> comp;
public ComparatorEquivalenceRelation(final Comparator<E> comparator)
{
this.comp = comparator;
}
public boolean areEqual(E e1, E e2)
{
return comparator.compare(e1,e2) == 0;
}

public boolean equals(Object o)
{
return o instanceof ComparatorEquivalenceRelation &&
((ComparatorEquivalenceRelation)o).comp.equals(this.comp);
}
}

}
---

Paul
--
Ich beantworte Java-Fragen per E-Mail nur gegen Bezahlung. Kostenpunkt
100 Euro pro angefangene Stunde. Bitte erwähne in der Anfrage, dass du
das akzeptierst, da ich sonst die E-Mail einfach ignoriere.
(In der Newsgroup fragen ist billiger und oft auch schneller.)
Ingo R. Homann
2006-05-31 13:41:57 UTC
Permalink
Hi Paul,
Post by Paul Ebermann
Ich hatte mir ja mal das Konzept einer externen
Äquivalenzrelation ausgedacht, mit eingebautem
Und was hat man davon? Ich meine, spätestens hier...
Post by Paul Ebermann
/**
* Eine Ä-Relation, basierend auf einem Comparator.
*/
public static final class ComparatorEquivalenceRelation<E>
implements EquivalenceRelation<E>
{
private final Comparator<E> comp;
public ComparatorEquivalenceRelation(final Comparator<E> comparator)
{
this.comp = comparator;
}
public boolean areEqual(E e1, E e2)
{
return comparator.compare(e1,e2) == 0;
}
...
}
}
...zeigt sich doch, dass sich das alles problemlos auch mit einem
Comparator erledigen lässt.

Das einzige, was IMHO bei dir möglich ist, ist Objekte nicht nur
unterschiedlich vergleichbar zu machen (so wie mit verschiedenen
Comparators), sondern ihnen auch unterschiedliche HashCodes verpassen zu
können. Darin sehe ich allerdings keinen Sinn.

Davon abgesehen: Das Ganze zwingt dich doch zudem dazu, sämtliche
Collection- und Map-Typen noch einmal zu implementieren, damit sie statt
mit einem Comparator dann mit einer EquivalenceRelation funktionieren.

Und selbst dann ist mir nicht klar, was der Vorteil gegenüber den
Standard-Collections ist.

Ciao,
Ingo
Ingo R. Homann
2006-05-31 13:45:34 UTC
Permalink
Korrigiere mich bitte, wenn ich was übersehen habe.

PS: Dass es damit möglich wäre, dieselben Objekte in unterschiedliche
HashMaps zu packen, die unterschiedliche HashCodes verwenden, so dass
der Zugriff geringfügig beschleunigt werden kann, wenn man viele Objekte
hat, die sich nach dem einen Vergleichskriterium nicht und nach dem
anderen doch unterscheiden (weil man Kollisionen vermeiden könnte), ist
mir klar. Aber das ist ein Fall, der IMHO (A) sehr konstruiert und (B)
anders einfacher zu lösen wäre.
Paul Ebermann
2006-05-31 18:33:47 UTC
Permalink
Post by Ingo R. Homann
Hi Paul,
Post by Paul Ebermann
Ich hatte mir ja mal das Konzept einer externen
Äquivalenzrelation ausgedacht, mit eingebautem
Und was hat man davon? Ich meine, spätestens hier...
Post by Paul Ebermann
/**
* Eine Ä-Relation, basierend auf einem Comparator.
*/
public static final class ComparatorEquivalenceRelation<E>
implements EquivalenceRelation<E>
{
private final Comparator<E> comp;
public ComparatorEquivalenceRelation(final Comparator<E> comparator)
{
this.comp = comparator;
}
public boolean areEqual(E e1, E e2)
{
return comparator.compare(e1,e2) == 0;
}
...
}
}
...zeigt sich doch, dass sich das alles problemlos auch mit einem
Comparator erledigen lässt.
Nein. Das zeigt, dass jeder Comparator (d.h. jede Totalordnung)
eine Ä-Relation induziert, nicht aber, dass es zu jeder Ä-Relation
eine passende (und auch halbwegs sinnvolle) Totalordnung gibt.

(Nimm etwa an, ich will zwei List<Double>-Objekte (die nicht nur
mit 0 gefüllt sind) als gleich ansehen, wenn sie die gleiche
Anzahl an Elementen haben und die Elemente jeweils durch
Multiplikation mit einem gemeinsamen Faktor auseinander
hervorgehen, d.h. es gibt ein double lambda, so dass
list1.get(i) * lambda = list2.get(i) ist, für alle Indizees i.
[1]

Wie würde dann dein Comparator aussehen?)
Post by Ingo R. Homann
Das einzige, was IMHO bei dir möglich ist, ist Objekte nicht nur
unterschiedlich vergleichbar zu machen (so wie mit verschiedenen
Comparators), sondern ihnen auch unterschiedliche HashCodes verpassen zu
können. Darin sehe ich allerdings keinen Sinn.
Es geht darum, dass der Hashcode zur equals-Implementierung
passt - ansonsten kann man ihn ja nicht für eine Hashmap nutzen.
Post by Ingo R. Homann
Davon abgesehen: Das Ganze zwingt dich doch zudem dazu, sämtliche
Collection- und Map-Typen noch einmal zu implementieren, damit sie statt
mit einem Comparator dann mit einer EquivalenceRelation funktionieren.
Comparator hilft ja nur für SortedMap/SortedSet, d.h. in der Praxis
genau für TreeMap/TreeSet - und das arbeitet O(log(N)) statt O(1)
wie HashMap für die Standard-Operationen (angenommen, die hashCode,
equals() und compare() arbeiten in O(1)).

Wir fügen statt SortedMap (was mit Comparator arbeitet) eben
eine EquivalenceMap (was mit EquivalenceRelation arbeitet) hinzu,
und für eine Hash-Implementation brauchen wir noch eine
HashableEquivalenceRelation.
(Analog für Set).
Post by Ingo R. Homann
Und selbst dann ist mir nicht klar, was der Vorteil gegenüber den
Standard-Collections ist.
- Man kann direkt mit BigDecimals in einer HashMap arbeiten,
ohne die dummen Nachteile (1.00 != 1.0) in Kauf nehmen zu
müssen.
- Wie gesagt: Es gibt Ä-Relationen, für die es keine passende
Totalordnung gibt - die Standard-Ä-Relation für BigDecimal
ist noch so ein Beispiel ...


Paul


[1] Das heißt, aus dem Vektorraum R^N machen wir den
projektiven Raum P^(N-1).
--
The Java Language Specification: http://java.sun.com/docs/books/jls/index.htmls
Ingo R. Homann
2006-06-01 12:05:37 UTC
Permalink
Hi Paul,
Post by Paul Ebermann
Post by Ingo R. Homann
...zeigt sich doch, dass sich das alles problemlos auch mit einem
Comparator erledigen lässt.
Nein. Das zeigt, dass jeder Comparator (d.h. jede Totalordnung)
eine Ä-Relation induziert, nicht aber, dass es zu jeder Ä-Relation
eine passende (und auch halbwegs sinnvolle) Totalordnung gibt.
(Nimm etwa an, ich will zwei List<Double>-Objekte (die nicht nur
mit 0 gefüllt sind) als gleich ansehen, wenn sie die gleiche
Anzahl an Elementen haben und die Elemente jeweils durch
Multiplikation mit einem gemeinsamen Faktor auseinander
hervorgehen, d.h. es gibt ein double lambda, so dass
list1.get(i) * lambda = list2.get(i) ist, für alle Indizees i.
[1]
Wie würde dann dein Comparator aussehen?)
Nun, IMHO ist das ein ziemlich (nein, sogar extremst) konstruiertes
Beispiel.

Ein Comparator ließe sich aber IMHO trotzdem darüber konstruieren:

double f:=list1.get(0)/list2.get(0);
for(int i=1;i<list1.length();i++) {
double d=list1.get(i)/list2.get(i)-f;
if(d!=0) {
return d;
}
}
return 0;

Dem aufmerksamen Leser sei es überlassen, Sonderfälle (ungleich lange
Listen, div-by-0, etc) auszuarbeiten und die Transitivität etc. zu
beweisen oder zu widerlegen! ;-)

Falls ihm letzteres nicht gelingt (und das also wirklich ein gültiger
Comparator sein sollte), so wird er sicher einwenden, dass dieser
Comparator zwar in sich konsistent sein mag, aber in der Praxis wenig
Sinn macht.

Schlimmer als die konstruierte Aufgabenstellung ist es IMHO aber nicht.
Post by Paul Ebermann
Post by Ingo R. Homann
Das einzige, was IMHO bei dir möglich ist, ist Objekte nicht nur
unterschiedlich vergleichbar zu machen (so wie mit verschiedenen
Comparators), sondern ihnen auch unterschiedliche HashCodes verpassen zu
können. Darin sehe ich allerdings keinen Sinn.
Es geht darum, dass der Hashcode zur equals-Implementierung
passt - ansonsten kann man ihn ja nicht für eine Hashmap nutzen.
Ja. Und damit kannst Du, wenn Du unterschiedliche HashCodes verwenden
willst, wie ich schrieb, ein paar Millisekunden rausschlagen, indem Du
Kollisionen vermeidest. Oder was erreichst Du damit? (Ich meine - einen
zu equals konsistenten HashCode zu generieren, ist ja erstmal nicht schwer.)
Post by Paul Ebermann
Post by Ingo R. Homann
Davon abgesehen: Das Ganze zwingt dich doch zudem dazu, sämtliche
Collection- und Map-Typen noch einmal zu implementieren, damit sie statt
mit einem Comparator dann mit einer EquivalenceRelation funktionieren.
Comparator hilft ja nur für SortedMap/SortedSet, d.h. in der Praxis
genau für TreeMap/TreeSet - und das arbeitet O(log(N)) statt O(1)
wie HashMap für die Standard-Operationen (angenommen, die hashCode,
equals() und compare() arbeiten in O(1)).
Ich weiss. Aber...
Post by Paul Ebermann
Wir fügen statt SortedMap (was mit Comparator arbeitet) eben
eine EquivalenceMap (was mit EquivalenceRelation arbeitet) hinzu,
und für eine Hash-Implementation brauchen wir noch eine
HashableEquivalenceRelation.
(Analog für Set).
...
Post by Paul Ebermann
- Wie gesagt: Es gibt Ä-Relationen, für die es keine passende
Totalordnung gibt - die Standard-Ä-Relation für BigDecimal
ist noch so ein Beispiel ...
...
Post by Paul Ebermann
[1] Das heißt, aus dem Vektorraum R^N machen wir den
projektiven Raum P^(N-1).
...
Post by Paul Ebermann
- Man kann direkt mit BigDecimals in einer HashMap arbeiten,
ohne die dummen Nachteile (1.00 != 1.0) in Kauf nehmen zu
müssen.
...du bringst hier fast nur theoretische, mathematische Konstrukte.
Alleine dein letzter Satz hier beantwortet meine Frage, was das in der
Praxis bringt.

Und auch hier ist mir das nicht klar: Schließlich kannst Du einer
HashMap ja nicht deine Ä-Relation übergeben, sprich, Du müsstest - wie
ich in meinem letzten Posting bereits schrieb - tatsächlich die HashMap
neu implementieren. Da erscheint es mir (deutlich) leichter, einfach
MyBigDecimals zu nehmen, bei denen equals überschrieben ist (oder
meinetwegen eine Realsierung über Delegation).

Ciao,
Ingo
Paul Ebermann
2006-06-02 00:01:23 UTC
Permalink
Post by Ingo R. Homann
Post by Paul Ebermann
Post by Ingo R. Homann
...zeigt sich doch, dass sich das alles problemlos auch mit einem
Comparator erledigen lässt.
Nein. Das zeigt, dass jeder Comparator (d.h. jede Totalordnung)
eine Ä-Relation induziert, nicht aber, dass es zu jeder Ä-Relation
eine passende (und auch halbwegs sinnvolle) Totalordnung gibt.
(Nimm etwa an, ich will zwei List<Double>-Objekte (die nicht nur
mit 0 gefüllt sind) als gleich ansehen, wenn sie die gleiche
Anzahl an Elementen haben und die Elemente jeweils durch
Multiplikation mit einem gemeinsamen Faktor auseinander
hervorgehen, d.h. es gibt ein double lambda, so dass
list1.get(i) * lambda = list2.get(i) ist, für alle Indizees i.
[1]
Wie würde dann dein Comparator aussehen?)
Nun, IMHO ist das ein ziemlich (nein, sogar extremst) konstruiertes
Beispiel.
Hmm, in der Mathematik kommen solche Äquivalenzklassenbildungen
schon mal vor.
Post by Ingo R. Homann
double f:=list1.get(0)/list2.get(0);
for(int i=1;i<list1.length();i++) {
double d=list1.get(i)/list2.get(i)-f;
if(d!=0) {
return d;
}
}
return 0;
Dem aufmerksamen Leser sei es überlassen, Sonderfälle (ungleich lange
Listen, div-by-0, etc)
... int-Rückgabetyp ;-) ...
Post by Ingo R. Homann
auszuarbeiten und die Transitivität etc. zu
beweisen oder zu widerlegen! ;-)
Tja, das hängt davon ab, wie genau man die Sonderfälle
behandelt :-)

Klar, irgend eine lexikographische Ordnung bekommt
man immer hin, aber ...
Post by Ingo R. Homann
Falls ihm letzteres nicht gelingt (und das also wirklich ein gültiger
Comparator sein sollte), so wird er sicher einwenden, dass dieser
Comparator zwar in sich konsistent sein mag, aber in der Praxis wenig
Sinn macht.
... man möchte doch etwas haben, was sinnvoll ist.

Hier noch ein Beispiel: Zwei Sets a und b sind gleich,
wenn sie die selben Elemente enthalten, d.h. wenn jedes
Element von a auch in b enthalten ist und umgekehrt.
Auf die Reihenfolge kommt es dabei nicht an.

OK, das regelt schon die equals-Methode, aber stell dir
vor, die würde anders aussehen (oder wir wollen so eine
Ä-Relation für beliebige Collections haben).

Hier eine vernünftige Comparator-Implementation zu finden,
ist etwas aufwendiger, erst recht, wenn wir für die Elemente
keinen Comparator haben ...
Post by Ingo R. Homann
Schlimmer als die konstruierte Aufgabenstellung ist es IMHO aber nicht.
Post by Paul Ebermann
Post by Ingo R. Homann
Das einzige, was IMHO bei dir möglich ist, ist Objekte nicht nur
unterschiedlich vergleichbar zu machen (so wie mit verschiedenen
Comparators), sondern ihnen auch unterschiedliche HashCodes verpassen zu
können. Darin sehe ich allerdings keinen Sinn.
Es geht darum, dass der Hashcode zur equals-Implementierung
passt - ansonsten kann man ihn ja nicht für eine Hashmap nutzen.
Ja. Und damit kannst Du, wenn Du unterschiedliche HashCodes verwenden
willst, wie ich schrieb, ein paar Millisekunden rausschlagen, indem Du
Kollisionen vermeidest. Oder was erreichst Du damit? (Ich meine - einen
zu equals konsistenten HashCode zu generieren, ist ja erstmal nicht schwer.)
Es geht hier eher darum, gleiche Hashcodes für äquivalente Objekte
zu haben (wenn unsere Ä-Relation gröber als die von .equals() ist),
ohne einen total trivialen (und damit unnützen) Hashcode zu haben.

(Den könnte man in diesem Falle aus den Quotienten a[i]/a[0]
bilden, glaube ich.)
Post by Ingo R. Homann
Post by Paul Ebermann
Post by Ingo R. Homann
Davon abgesehen: Das Ganze zwingt dich doch zudem dazu, sämtliche
Collection- und Map-Typen noch einmal zu implementieren, damit sie statt
mit einem Comparator dann mit einer EquivalenceRelation funktionieren.
Comparator hilft ja nur für SortedMap/SortedSet, d.h. in der Praxis
genau für TreeMap/TreeSet - und das arbeitet O(log(N)) statt O(1)
wie HashMap für die Standard-Operationen (angenommen, die hashCode,
equals() und compare() arbeiten in O(1)).
Ich weiss. Aber...
...
Post by Paul Ebermann
- Wie gesagt: Es gibt Ä-Relationen, für die es keine passende
Totalordnung gibt - die Standard-Ä-Relation für BigDecimal
ist noch so ein Beispiel ...
[...]
Post by Ingo R. Homann
Post by Paul Ebermann
- Man kann direkt mit BigDecimals in einer HashMap arbeiten,
ohne die dummen Nachteile (1.00 != 1.0) in Kauf nehmen zu
müssen.
...du bringst hier fast nur theoretische, mathematische Konstrukte.
Ich bin eben Mathematiker :-)

Was kann ich dafür, dass hier keiner Anwendungsbeispiele aus
anderen Fachgebieten liefert, ich glaube nicht, dass
Äquivalenzrelationen nur in der Mathematik vorkommen.
Post by Ingo R. Homann
Alleine dein letzter Satz hier beantwortet meine Frage, was das in der
Praxis bringt.
Post by Paul Ebermann
Wir fügen statt SortedMap (was mit Comparator arbeitet) eben
eine EquivalenceMap (was mit EquivalenceRelation arbeitet) hinzu,
und für eine Hash-Implementation brauchen wir noch eine
HashableEquivalenceRelation.
(Analog für Set).
Und auch hier ist mir das nicht klar: Schließlich kannst Du einer
HashMap ja nicht deine Ä-Relation übergeben, sprich, Du müsstest - wie
ich in meinem letzten Posting bereits schrieb - tatsächlich die HashMap
neu implementieren.
Ja, das mache ich aber nur einmal.

Habe ich übrigens schon vor einigen Jahren gemacht,
das ist nicht so schwer.

(Oder wir überzeugen Sun, dass java.util.HashMap eine
Serie zusätzlicher Konstruktoren bekommt, und die
bisherigen einfach DEFAULT_EQUIVALENCE verwenden - man
kann dann sogar noch ein paar private Methoden einsparen.)
Post by Ingo R. Homann
Da erscheint es mir (deutlich) leichter, einfach
MyBigDecimals zu nehmen, bei denen equals überschrieben ist (oder
meinetwegen eine Realsierung über Delegation).
Delegation ist hier besser, wegen der Symmetrie. (Und man
muss auch .hashCode neu implementieren, sonst gibt das
Probleme.)


Paul

PS: Ich bin die nächsten paar Tage in Braunschweig und ohne
Netzzugang, kann also erst mal nicht antworten. Ich bemühe mich,
nachher die Threads zu lesen ...
--
Ich beantworte Java-Fragen per E-Mail nur gegen Bezahlung. Kostenpunkt
100 Euro pro angefangene Stunde. Bitte erwähne in der Anfrage, dass du
das akzeptierst, da ich sonst die E-Mail einfach ignoriere.
(In der Newsgroup fragen ist billiger und oft auch schneller.)
Ingo Menger
2006-06-02 09:15:04 UTC
Permalink
Post by Paul Ebermann
Post by Ingo R. Homann
Da erscheint es mir (deutlich) leichter, einfach
MyBigDecimals zu nehmen, bei denen equals überschrieben ist (oder
meinetwegen eine Realsierung über Delegation).
Delegation ist hier besser, wegen der Symmetrie. (Und man
muss auch .hashCode neu implementieren, sonst gibt das
Probleme.)
Es ist ja schon lustig, daß Sun selbst empfiehlt, daß folgende
Gesetze gelten sollten:
a == b => a.hascode() == b.hashcode()
a == b <=> a.compareTo(b) == 0
und dann warnt, daß es bei BigDecimal anders ist.
Warum korrigieren sie es nicht einfach, und fertig?
Ich kann mir nicht vorstellen (oder möchte mir eigentlich lieber nicht
vorstellen), daß es wichtige Anwendungen gibt, deren Funktion davon
abhängt, daß 5.0 != 5.000
Ralf Ullrich
2006-06-02 10:09:56 UTC
Permalink
Post by Ingo Menger
Post by Paul Ebermann
Post by Ingo R. Homann
Da erscheint es mir (deutlich) leichter, einfach
MyBigDecimals zu nehmen, bei denen equals überschrieben ist (oder
meinetwegen eine Realsierung über Delegation).
Delegation ist hier besser, wegen der Symmetrie. (Und man
muss auch .hashCode neu implementieren, sonst gibt das
Probleme.)
Es ist ja schon lustig, daß Sun selbst empfiehlt, daß folgende
a == b => a.hascode() == b.hashcode()
a == b <=> a.compareTo(b) == 0
und dann warnt, daß es bei BigDecimal anders ist.
Warum korrigieren sie es nicht einfach, und fertig?
Ich kann mir nicht vorstellen (oder möchte mir eigentlich lieber nicht
vorstellen), daß es wichtige Anwendungen gibt, deren Funktion davon
abhängt, daß 5.0 != 5.000
Also eure BigDecimal-equals-Diskussion, übersieht eines völlig: Die equals
Implementation von BigDecimal mag counterintuitive sein, aber sie ist
korrekt!

Im übrigen ist das auch nichts neues:

1e-7d == 1e-7f

ergibt schließlich auch false.

Zwei Zahlen, die nicht dieselbe Präzision haben, sind eben nur
ausnahmsweise gleich. Man sollte sich ins Gedächtnis rufen, dass
Gleitkommazahlen lediglich einen mit der entsprechenden Bittiefe
repräsentierbaren Wert aus einem Intervall darstellen. Es mag dann zwar
zwischen zwei Präzisionen der repräsentierbare Wert übereinstimmen,
deswegen aber noch lange nicht das Intervall. Und bei BigDecimal ist das
im Prinzip genauso. Berechnungen mit BigDecimals sind zwar zunächst mal
exakt und damit keine Intervalle, die kritisierten ungleichen Werte treten
aber in der Regel erst auf, wenn man bei Rechenoperationen rundet. Und das
heißt, die Werte sind dann eben doch eigentlich Intervalle. Und das
Intervall [4.95..5.05[ ist eben nicht gleich dem Intervall [4.9995..5.0005[.


Just my 2 cents,

cu
Ingo R. Homann
2006-06-02 10:58:46 UTC
Permalink
Hi Ralf,
Post by Ralf Ullrich
Zwei Zahlen, die nicht dieselbe Präzision haben, sind eben nur
ausnahmsweise gleich. Man sollte sich ins Gedächtnis rufen, dass
Gleitkommazahlen lediglich einen mit der entsprechenden Bittiefe
repräsentierbaren Wert aus einem Intervall darstellen. Es mag dann zwar
zwischen zwei Präzisionen der repräsentierbare Wert übereinstimmen,
deswegen aber noch lange nicht das Intervall. Und bei BigDecimal ist das
im Prinzip genauso. Berechnungen mit BigDecimals sind zwar zunächst mal
exakt und damit keine Intervalle, die kritisierten ungleichen Werte
treten aber in der Regel erst auf, wenn man bei Rechenoperationen
rundet. Und das heißt, die Werte sind dann eben doch eigentlich
Intervalle. Und das Intervall [4.95..5.05[ ist eben nicht gleich dem
Intervall [4.9995..5.0005[.
Das mag zwar alles stimmen, aber in der Praxis sieht es doch eher so aus:

Ich habe ein BigDecimal, welches ich auf 0 testen will. Oft wurde da gar
nichts geteilt und gar nichts gerundet, sondern z.B. nur Beträge addiert.

Trotzdem muss ich jetzt überlegen, ob ich auf 0 oder 0.0 oder 0.00
testen muss.

Diesen Bug halte ich für viel kritischer als den von dir angesprochenen.

Ciao,
Ingo
Ralf Ullrich
2006-06-02 12:15:18 UTC
Permalink
Post by Ingo R. Homann
Ich habe ein BigDecimal, welches ich auf 0 testen will. Oft wurde da gar
nichts geteilt und gar nichts gerundet, sondern z.B. nur Beträge addiert.
Trotzdem muss ich jetzt überlegen, ob ich auf 0 oder 0.0 oder 0.00 testen
muss.
Erstmal, musst du bei Addition und Subtraktion, diese Überlegung nur
anstellen, wenn deine beteiligten BigDecimals unterschiedlich viele
Stellen hinterm Komma hatten. Wenn du nach diesem Kriterium normierte
BigDecimals verwendest ist auch dein Ergebnis nach Addition und
Subtraktion wieder in der Norm.

Aber selbst für den Fall, dass ...:

BigDecimal a,b = ...

// test1
if (b.compareTo(BigDecimal.ZERO) == 0) {}

// test2
if (b.equals(new BigDecimal(BigInteger.ZERO,b.scale()))) {}

// test3
if (BigInteger.ZERO.equals(b.unscaledValue())) {}

// test4
if (b.signum() == 0) {}

Und das sind nur die Möglichkeiten, die mir spontan einfallen.


Aber eigentlich wollte ich keine neue Diskussion anstoßen, ob das jetzt
toll ist oder nicht, sondern nur darauf hinweisen, das BigDecimal#equals
durchaus korrekt implementiert ist, auch wenn es unserer Intuition
entgegenläuft.

Ganz korrekt wäre es IMHO übrigens erst, wenn BigDecimals mit
verschiedener Scale quasi wie verschiedene Klassen behandelt würden. Dann
wäre die Addition z.B. nur noch zwischen BigDecimals derselben Klasse
definiert. Und für "1 + 1.0" bräuchte man einen cast, entweder in die eine
oder andere Richtung.


Aber vielleicht schaffen wir es ja demnächst noch, dass Computer nach
fünfzig Jahren endlich richtig rechnen lernen, dann sind solche counter
intuitive Implementationen vielleicht nicht mehr nötig. (Stichwort
Intervall-Arithmetik)

cu
Ingo R. Homann
2006-06-02 13:42:36 UTC
Permalink
Hi,
Post by Ralf Ullrich
Aber eigentlich wollte ich keine neue Diskussion anstoßen, ob das jetzt
toll ist oder nicht, sondern nur darauf hinweisen, das BigDecimal#equals
durchaus korrekt implementiert ist, auch wenn es unserer Intuition
entgegenläuft.
Das es formal korrekt (und konsistent) implementiert ist, hat nie jemand
bestritten.

Aber das wäre die andere (intuitive) Implementierung doch auch - eine
entsprechende Implementierung von HashCode vorausgesetzt (die habe ich
gerade nicht im Kopf). Oder?

Also...
Post by Ralf Ullrich
Intervall-Arithmetik
...wozu dieser Aufwand, anstatt es einfach UND intuitiv zu implementieren?

Ciao,
Ingo
Ingo Menger
2006-06-02 14:00:04 UTC
Permalink
Post by Ralf Ullrich
Post by Ingo Menger
Post by Paul Ebermann
Post by Ingo R. Homann
Da erscheint es mir (deutlich) leichter, einfach
MyBigDecimals zu nehmen, bei denen equals überschrieben ist (oder
meinetwegen eine Realsierung über Delegation).
Delegation ist hier besser, wegen der Symmetrie. (Und man
muss auch .hashCode neu implementieren, sonst gibt das
Probleme.)
Es ist ja schon lustig, daß Sun selbst empfiehlt, daß folgende
a == b => a.hascode() == b.hashcode()
a == b <=> a.compareTo(b) == 0
und dann warnt, daß es bei BigDecimal anders ist.
Warum korrigieren sie es nicht einfach, und fertig?
Ich kann mir nicht vorstellen (oder möchte mir eigentlich lieber nicht
vorstellen), daß es wichtige Anwendungen gibt, deren Funktion davon
abhängt, daß 5.0 != 5.000
Also eure BigDecimal-equals-Diskussion, übersieht eines völlig: Die equals
Implementation von BigDecimal mag counterintuitive sein, aber sie ist
korrekt!
Nicht nach dem Kontrakt, den equals() with respect to compareTo() laut
Sun haben sollte.
Post by Ralf Ullrich
1e-7d == 1e-7f
ergibt schließlich auch false.
Dann ist aber auch compareTo() != 0. Das ist der Punkt.
Post by Ralf Ullrich
Zwei Zahlen, die nicht dieselbe Präzision haben, sind eben nur
ausnahmsweise gleich.
Das ist im Prinzip Definitionssache, aber bei Dezimalzahlen ist es
schon definiert, und zwar so, daß 0, 0.0, 0.00, 0.000, 0.0000 usw.
alle gleich sind. Dies ergibt sich aus der Definition von a != b <=>
exists(c), c!=0 && (a+c == b).
(Und bei float und double gibt es genau so eine Zahl, d.h. 1e-7d -
1e-7f sollte ungleich 0.0 sein.)
Post by Ralf Ullrich
Man sollte sich ins Gedächtnis rufen, dass
Gleitkommazahlen lediglich einen mit der entsprechenden Bittiefe
repräsentierbaren Wert aus einem Intervall darstellen.
Richtig.
Post by Ralf Ullrich
Und bei BigDecimal ist das
im Prinzip genauso.
Nicht im mindesten. Immerhin wäre es denkbar, daß man ein BigDecimal
einfach aus einem Paar von BigIntegern baut (Zähler und Nenner), oder
sonstwie exakte Rechnung ermöglicht.
Post by Ralf Ullrich
Berechnungen mit BigDecimals sind zwar zunächst mal
exakt und damit keine Intervalle, die kritisierten ungleichen Werte treten
aber in der Regel erst auf, wenn man bei Rechenoperationen rundet.
Ja, aber ohne Not. Daß 3 != 3.003 ist, ist klar. Wenn aber 3.003 auf 2
Stellen gerundet wird, ist das nicht mehr 3.003, sondern 3.00, also 3 +
0/10 + 0/100 == 0.
Genausogut könnte man fordern halbiere(2) soll nicht gleich 1 sein,
denn vor der Anwendung von "halbiere()" wäre es ja auch nicht 1
gewesen.
Wenn man mit Intervallen rechnen will, sollte man einen entsprechenden
Datentyp nehmen.
Paul Ebermann
2006-06-05 15:51:10 UTC
Permalink
Post by Ingo Menger
Post by Ralf Ullrich
Also eure BigDecimal-equals-Diskussion, übersieht eines völlig: Die equals
Implementation von BigDecimal mag counterintuitive sein, aber sie ist
korrekt!
Nicht nach dem Kontrakt, den equals() with respect to compareTo() laut
Sun haben sollte.
Bei Comparable wurde dieser Teil des Kontraktes
("consistent with .equals") bewusst optional gehalten
(wenn auch empfohlen) - und bei equals() steht nichts
davon :-)
Post by Ingo Menger
Post by Ralf Ullrich
1e-7d == 1e-7f
ergibt schließlich auch false.
Dann ist aber auch compareTo() != 0. Das ist der Punkt.
... und das Problem rührt nur von der Dezimaldarstellung
der Literale her.

Wenn man

double d = 1e-7f;
float f = 1e-7f;

schreibt, ergibt

d == f

nachher true, obwohl d mit einer viel größeren Präzision
gespeichert wird als f.
Post by Ingo Menger
Post by Ralf Ullrich
Zwei Zahlen, die nicht dieselbe Präzision haben, sind eben nur
ausnahmsweise gleich.
Das ist im Prinzip Definitionssache, aber bei Dezimalzahlen ist es
schon definiert, und zwar so, daß 0, 0.0, 0.00, 0.000, 0.0000 usw.
alle gleich sind. Dies ergibt sich aus der Definition von a != b <=>
exists(c), c!=0 && (a+c == b).
Naja, dies kann man nicht als Definition von != verwenden,
wenn auf der rechten Seite auch != benutzt wird.
Post by Ingo Menger
Post by Ralf Ullrich
Man sollte sich ins Gedächtnis rufen, dass
Gleitkommazahlen lediglich einen mit der entsprechenden Bittiefe
repräsentierbaren Wert aus einem Intervall darstellen.
Richtig.
Post by Ralf Ullrich
Und bei BigDecimal ist das
im Prinzip genauso.
Nicht im mindesten. Immerhin wäre es denkbar, daß man ein BigDecimal
einfach aus einem Paar von BigIntegern baut (Zähler und Nenner), oder
sonstwie exakte Rechnung ermöglicht.
Nein, das wäre dann BigRational o.ä.

BigDecimal ist auch eine Gleitkommazahl, bloß eben in
Dezimal- statt Binärsystem, und mit variabler "Bitbreite".


Paul
--
Ich beantworte Java-Fragen per E-Mail nur gegen Bezahlung. Kostenpunkt
100 Euro pro angefangene Stunde. Bitte erwähne in der Anfrage, dass du
das akzeptierst, da ich sonst die E-Mail einfach ignoriere.
(In der Newsgroup fragen ist billiger und oft auch schneller.)
Paul Ebermann
2006-06-05 15:38:33 UTC
Permalink
Post by Ingo Menger
Post by Paul Ebermann
Post by Ingo R. Homann
Da erscheint es mir (deutlich) leichter, einfach
MyBigDecimals zu nehmen, bei denen equals überschrieben ist (oder
meinetwegen eine Realsierung über Delegation).
Delegation ist hier besser, wegen der Symmetrie. (Und man
muss auch .hashCode neu implementieren, sonst gibt das
Probleme.)
Es ist ja schon lustig, daß Sun selbst empfiehlt, daß folgende
a == b => a.hascode() == b.hashcode()
a == b <=> a.compareTo(b) == 0
(links mit .equals(), nicht mit ==)
Post by Ingo Menger
und dann warnt, daß es bei BigDecimal anders ist.
Warum korrigieren sie es nicht einfach, und fertig?
Ich könnte mir vorstellen, dass da das Konzept hintersteckt,
dass ein BigDecimal-Objekt aus Wert und Genauigkeit besteht -
und man diese beiden Werte einzeln manipulieren kann.

Es wäre vielleicht etwas verwirrend, wenn man zwei Objekte
hat, die laut .equals() gleich sind, sich dabei aber merklich
anders verhalten.

Außerdem ist auf die Weise die Implementierung von .hashCode()
und .equals() deutlich einfacher, als wenn man es modulo
Präzision machen würde.

(.compareTo() kann man nicht vernünftig anders implementieren.)


Paul
--
Alle meine Antworten, Feststellungen, Behauptungen
sind mit AFAIK und IMHO zu lesen und nicht in der
Luft- und Raumfahrt oder Atomindustrie zu verwenden.
Michael Paap
2006-05-31 14:48:09 UTC
Permalink
Post by Paul Ebermann
* Berechnet einen Häcksel-Code des Objektes E.
Nachwirkungen einer Überdosis von Stefan-Arbeitsspeicher-Postings?

SCNR
Michael
Paul Ebermann
2006-05-31 18:11:27 UTC
Permalink
Post by Michael Paap
Post by Paul Ebermann
* Berechnet einen Häcksel-Code des Objektes E.
Nachwirkungen einer Überdosis von Stefan-Arbeitsspeicher-Postings?
Nein, Stefan verwendet immer "Streuwert".

Ich hatte vor ein paar Jahren mal im Wörterbuch
nachgeschlagen, was "hash" eigentlich wirklich
bedeutet :-)


Paul
--
The Java Virtual Machine Specification:
http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html
Lars Amsel
2006-05-31 19:22:00 UTC
Permalink
Post by Paul Ebermann
Post by Paul Ebermann
* Berechnet einen Häcksel-Code des Objektes E.
[...]
Ich hatte vor ein paar Jahren mal im Wörterbuch
nachgeschlagen, was "hash" eigentlich wirklich
bedeutet :-)
Häcksel-Code ist dann aber nicht konsequent zu Ende gedacht.
^
Gruß

Lars
Frank Buss
2006-05-31 19:43:33 UTC
Permalink
Post by Paul Ebermann
Ich hatte vor ein paar Jahren mal im Wörterbuch
nachgeschlagen, was "hash" eigentlich wirklich
bedeutet :-)
Also das Google-Sprachtool gibt für "Hash Code" die Übersetzung
"Durcheinander Code" aus. Eine schöne Übersetzung wäre also "Durcheinander
Chiffre" oder einfach "Durcheinanderzahl" :-)
--
Frank Buss, ***@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
Stefan Ram
2006-05-31 21:41:08 UTC
Permalink
Post by Paul Ebermann
Ich hatte vor ein paar Jahren mal im Wörterbuch
nachgeschlagen, was "hash" eigentlich wirklich
bedeutet (...)
Das Wort stammt von einem deutschen Wort ab (wie ein großer
Teil des englischen Vokabulars), nämlich vom althochdeutschen
"happa" (Sichel). Ein zeitgenössisches Wort, das damit verwandt
ist, ist die "Hippe" im Sinne von "Messer" oder "Sense".
Frank Buss
2006-05-31 22:06:46 UTC
Permalink
Post by Stefan Ram
Das Wort stammt von einem deutschen Wort ab (wie ein großer
Teil des englischen Vokabulars), nämlich vom althochdeutschen
"happa" (Sichel). Ein zeitgenössisches Wort, das damit verwandt
ist, ist die "Hippe" im Sinne von "Messer" oder "Sense".
Also laut Oxford English Dictionary stammt es von dem französischen Wort
"hacher" (Axt) ab:

http://www.askoxford.com/concise_oed/hash_1?view=uk

Und das passendste Anwendungsbeispiel dort:

make a hash of = make a mess of

Also Durcheinander oder auch Unordnung, was dann noch schöner klingt:
Unordnungs-Chiffre. Aber du kannst es natürlich auch Sichel-Zahl nennen :-)
--
Frank Buss, ***@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
Stefan Ram
2006-06-01 03:19:34 UTC
Permalink
Post by Frank Buss
Also laut Oxford English Dictionary stammt es von dem
« hacher » ist ein Verb, die Axt ist « hache ».

Und dazu finde ich im Dictionnaire de l'Académie française,
neuvième édition:

'HACHE n. f. XIIe siècle. Issu de l'ancien haut allemand
happja, « instrument tranchant ».

http://atilf.atilf.fr/dendien/scripts/generic/affiche.exe?48;s=1153124790;d=1;f=4;t=4;r=4;
Lesen Sie weiter auf narkive:
Loading...